Interfaces have become the interface is that it did not materialize, but the statement. But then everything changed, Java appeared the default method, C #, there have been the default method. The interface is not as interfaces in the traditional sense, the concept began to move towards the abstract class, a pure abstract things, the sudden appearance of the entity, began innocently not tell.
The world has changed, but he is beginning to change how it?
1. Origin
Although this article mentioned Java, but in recent years I mainly write C # program, it is not explicitly naming the language will be more inclined to C # specification of some updating.
Once, we define IStringList
an interface that declares a list:
This is just an example, in order to avoid the introduction of additional technical concept, there is no use of generic example.
interface IStringList {
void Add(string o); // 添加元素
void Remove(int i); // 删除元素
string Get(int i); // 获取元素
int Length { get; } // 获取列表长度
}
Anyway, this list already has the basic add delete change search features, such as traversal, you can write
IStringList list = createList();
for (var i = 0; i < list.Length; i++) {
string o = list.Get(i);
// Do something with o
}
This IStringList
after the release as a base class library interface, a large number of programmers using this interface, a bunch of various kinds of lists, like StringArrayList
, , LinkedStringList
, , ......StringQueue
there is an abstract class, there are expansion interfaces, but also various implementation class. In short, after a long period of accumulation, the children and grandchildren around the world.StringStack
SortedStringList
IStringList
Then IStringList
the inventor, decided to define a list of more ways to suit developers of the rapid development in technology IStringList
requires ease of use, so
interface IStringList {
int IndexOf(string o); // 查找元素的索引,未找到返回 -1
void Insert(string o, int i); // 在指定位置插入元素
// ------------------------------
void Add(string o); // 添加元素
void Remove(int i); // 删除元素
string Get(int i); // 获取元素
int Length { get; } // 获取列表长度
}
Of course, changes in addition to the interface implementation class must implement all of it, otherwise the compiler will complain, the abstract base class library AbstractStringList
implemented a new interface increases above. The whole basis of the Treasury perfect compilation, released version 2.0.
However, the reality is very cruel!
Base library users (developers) issued a great sound complain, because they are too many, but the code is compiled!
Yes, not all users will directly inherit AbstractStringList
, many users directly realized IStringList
. There are many users and even expanded IStringList
, but they are not defined int IndexOf(string o)
but defined int Find(string o)
. Because the underlying library interface IStringList
changes, users need to spend a lot of time to code to implement a IStringList
new method definition.
This example is mentioned IStringList
, only added two methods. Although this has little to no trouble caused by the user, but the workload still acceptable. But think about JDK and .NET Framework / Core vast library foundation, I am afraid that the user can only use the "collapse" to describe!
2. Measures
Certainly not allow users to collapse, need to find ways to solve this problem. So, Java and C # are two programs appeared
- Java presents the default method, the default implementation is added in the interface
- C # extension methods proposed, namely to pretend that by changing the form of a static method call is subject to call
I have to say that C # extension method is very smart, but it is after all not really expand on the interface, so in C # 8 also joined the default method to solve the problem caused by the expansion of the interface.
After the interface extension method proposed, it solves the problem of the default implementation, but brought out new problems.
- The default interface method, the class implements the interface needed to achieve it? If you do not realize what would happen?
- Whether Java or C # classes are not allowed multiple inheritance, but the interface can be. The default interface implementation brings a similar class multiple inheritance problems arising, how to do?
- In the complex implementation and inheritance relations, which in the end will be a final method of execution?
3. a relationship problem, the default implementation of methods and classes
Ignore the above IStringList
interface to add Insert(Object, int)
methods, we focus on IndexOf(Object)
on. Java and C # syntax similar to:
3.1. Let's look at the default method syntax
- Java version
interface StringList {
void add(Object s);
void remove(int i);
Object get(int i);
int getLength();
default int indexOf(Object s) {
for (int i = 0; i < getLength(); i++) {
if (get(i) == s) { return i; }
}
return -1;
}
}
- C # version
interface IStringList
{
public void Add(string s);
void Remove(int i);
string Get(int i);
int Length { get; }
int IndexOf(string s)
{
for (var i = 0; i < Length; i++)
{
if (Get(i) == s) { return i; }
}
return -1;
}
}
Here the C # and Java interfaces written down, mainly because both argument and naming conventions are slightly different. Research C # and Java behavior similar to local subsequently performed, it mainly focuses on C # a.
How to distinguish between the sample C # or Java Example? Look at the code specifications, most notably C # naming method with Pascal, Java method naming convention used camel. Of course, there are not the same arrow Lambda.
The next realization, only C # example:
class MyList : IStringList
{
List<string> list = new List<string>(); // 偷懒用现成的
public int Length => list.Count;
public void Add(string o) => list.Add(o);
public string Get(int i) => list[i];
public void Remove(int i) => list.RemoveAt(i);
}
MyList
Not implemented IndexOf
, but using them will not have any problems
class Program
{
static void Main(string[] args)
{
IStringList myList = new MyList();
myList.Add("First");
myList.Add("Second");
myList.Add("Third");
Console.WriteLine(myList.IndexOf("Third")); // 输出 2
Console.WriteLine(myList.IndexOf("first")); // 输出 -1,注意 first 大小写
}
}
3.2. IndexOf achieved in the MyList
Now, MyList
to add IndexOf
, to realize the search string case-insensitive:
// 这里用 partial class 表示是部分实现,
// 对不住 javaer,Java 没有部分类语法
partial class MyList
{
public int IndexOf(string s)
{
return list.FindIndex(el =>
{
return el == s
|| (el != null && el.Equals(s, StringComparison.OrdinalIgnoreCase));
});
}
}
Then Main
the contents of the output becomes the function
Console.WriteLine(myList.IndexOf("Third")); // 还是返回 2
Console.WriteLine(myList.IndexOf("first")); // 返回 0,不是 -1
Obviously this call MyList.IndexOf()
.
3.3 Conclusion, as well as Java and C # differences
The above mainly in C # as an example, in fact, Java is the same. The above example is invoked by the interface type IndexOf
method. The first call is IStringList.IndexOf
the default implementation, because at that time MyList
did not materialize IndexOf
; the second call is MyList.IndexOf
realized. I use Java to write similar code, exactly the same behavior.
So, for the default method, it will implement priority call class , if not implement an interface with the default method of the class, only to call the default interface methods.
but! ! ! The previous example is the type of interface used reference implementation, if replaced by instances of a class type to refer to an instance of it?
If MyList
the realized IndexOf
, the result makes no difference. However, if MyList
not implemented in IndexOf
time, Java and C # have on the difference in treatment.
Take a look at the C # Main
function, but the compiler ( Compiler Error CS1929 ), because MyList
not defined IndexOf
.
The Java do? Passed, as always, running the results!
C # from the point of view, MyList
since we know that there is IndexOf
an interface, it should realize it, but can not pretend not to know. But if by IStringList
to call IndexOf
, then it can be considered MyList
not aware of IndexOf
the interface, thus allowing calls the default interface. The interface or interfaces, do not know the new interface methods, did not realize, do not blame you; but you knew not realize, and that is your right.
But from the perspective of Java, MyList
the consumer is not necessarily MyList
the producers. From a consumer point of view, MyList
to achieve the StringList
interface, and there is the interface definition indexOf
method, so consumers call myList.indexOf
is reasonable.
Java's behavior is relatively loose, as long as you realize you use, do not control what is achieved.
The C # behavior more stringent when consumers use can be easily understood by the compiler's own use it is class implements, or the default interface implementations (although did not know how much use). In fact, if not implemented in the class inside, interface document would not have written out the interface with a smart prompt the editor does not pop up. Really want to write, it can be displayed to an interface to call:
Console.WriteLine(((IStringList)myList).IndexOf("Third"));
And according to the above test results, in the future MyList
to achieve the IndexOf
following, such a call is switched directly to the calling MyList
implementations, semantic no problem.
4. Question two, the issue of multiple inheritance
Whether Java or C # classes are not allowed multiple inheritance, but the interface can be. The default interface implementation brings a similar class multiple inheritance problems arising, how to do?
For example, people can walk, you can take the birds, then the "Monarch of Clouds" how to get there?
4.1. First look at the C #
No default implementation class interface:
interface IPerson
{
void Walk() => Console.WriteLine("IPerson.Walk()");
}
interface IBird
{
void Walk() => Console.WriteLine("IBird.Walk()");
}
class BirdPerson : IPerson, IBird { }
Result of the call:
BirdPerson birdPerson = new BirdPerson();
// birdPerson.Walk(); // CS1061,没有实现 Walk
((IPerson)birdPerson).Walk(); // 输出 IPerson.Walk()
((IBird)birdPerson).Walk(); // 输出 IBird.Walk()
Can not be used directly birdPerson.Walk()
in front of the truth has been spoken. But to call by different types of interfaces, the behavior is inconsistent, completely determined by the default method interface. This is understandable, since the class does not own implementation, then what interface reference, indicating that developers want to use the default behavior of the specified interface.
Put it bluntly, you are seen as the cloud monarch, his employer would take the law; you put cloud monarch seen as a bird, it's a bird walk.
However, if the class has achieved, the situation is different:
class BirdPerson : IPerson, IBird
{
// 注意这里的 public 可不能少
public void Walk() => Console.WriteLine("BirdPerson.Walk()");
}
BirdPerson birdPerson = new BirdPerson();
birdPerson.Test(); // 输出 BirdPerson.Walk()
((IPerson)birdPerson).Walk(); // 输出 BirdPerson.Walk()
((IBird)birdPerson).Walk(); // 输出 BirdPerson.Walk()
Exactly the same output, the default behavior defined in the interface, there is a time to achieve, when it does not exist in the class!
Monarch of Clouds personality: no matter how you look, I'll just leave.
The only caveat here is BirdPerson
implemented Walk()
must be declared public
, or C # will use it as an internal class behavior, not the behavior of the interface implementation. This C # and requirements for implementation of the interface method is the same: to implement interface member must be declaredpublic
.
4.2. Then look at the different Java
Go to the Java side, the situation is different, never let the compiler too
interface Person {
default void walk() {
out.println("IPerson.walk()");
}
}
interface Bird {
default void walk() {
out.println("Bird.walk()");
}
}
// Duplicate default methods named walk with the parameters () and ()
// are inherited from the types Bird and Person
class BirdPerson implements Person, Bird { }
This means that, Person
and Bird
are all the same signature walk
defines the default current method, the compiler does not know BirdPerson
in the end how to do it. So if only one walk
has a default to achieve it?
interface Person {
default void walk() {
out.println("IPerson.walk()");
}
}
interface Bird {
void walk();
}
// The default method walk() inherited from Person conflicts
// with another method inherited from Bird
class BirdPerson implements Person, Bird { }
This means that the two interfaces are inconsistent behavior, the compiler still do not know how to deal with BirdPerson
.
In short, anyway, it is to BirdPerson
have to implement their own walk()
. Since the BirdPerson
internal implementation of walk()
that call behavior it will not have much suspense:
BirdPerson birdPerson = new BirdPerson();
birdPerson.walk(); // 输出 BirdPerson.walk()
((Person) birdPerson).walk(); // 输出 BirdPerson.walk()
((Bird) birdPerson).walk(); // 输出 BirdPerson.walk()
4.3 Conclusion, multiple inheritance is no problem
If a class implements multiple interfaces are defined in the same method signature, not by default realize, of course, no problem.
If the class is implemented in the method signature, and that in any case, are calling this method, there will not be a problem.
But in the interface has a default implementation, but the class does not implement the situation, C # reference type to the actual behavior to deal with; Java directly being given to the developers to deal with. I much prefer C # practice, after all, the default method of mind is to not force the developer to handle the increased hassles interface methods.
5. Question three, more complex cases to analyze how
For more complex cases, most of the time or can be guessed how to call, after all, there is a basic principle in there.
5.1. Implement priority in class
For example, WalkBase
the definition of a Walk()
method, but did not implement any interfaces, BirdPerson
from WalkBase
inheritance, implements IPerson
the interface, but did not realize Walk()
method, then the implementation of which Walk
it?
Will perform WalkBase.Walk()
- no matter what the circumstances, like the preferred method !
class WalkBase
{
public void Walk() => Console.WriteLine("WalkBase.Walk()");
}
class BirdPerson : WalkBase, IPerson { }
static void Main(string[] args)
{
BirdPerson birdPerson = new BirdPerson();
birdPerson.Walk(); // 输出 WalkBase.Walk()
((IPerson)birdPerson).Walk(); // 输出 WalkBase.Walk()
}
If the parent class has a subclass to achieve, but not a subclass of "heavy-duty", but "cover" to achieve, you should come to the nearest class according to reference types, such as
class WalkBase : IBird // <== 注意这里实现了 IBird
{
public void Walk() => Console.WriteLine("WalkBase.Walk()");
}
class BirdPerson : WalkBase, IPerson // <== 这里_没有_实现 IBird
{
// 注意:这里是 new,而不是 override
public new void Walk() => Console.WriteLine("BirdPerson.Walk()");
}
static void Main(string[] args)
{
BirdPerson birdPerson = new BirdPerson();
birdPerson.Walk(); // 输出 BirdPerson.Walk()
((WalkBase)birdPerson).Walk(); // 输出 WalkBase.Walk()
((IPerson)birdPerson).Walk(); // 输出 BirdPerson.Walk()
((IBird)birdPerson).Walk(); // 输出 WalkBase.Walk()
}
If WalkBase
to virtual
define Walk()
, and BirdPerson
to override
define Walk()
, that there is no suspense output all BirdPerson.Walk()
.
class WalkBase : IBird
{
public virtual void Walk() => Console.WriteLine("WalkBase.Walk()");
}
class BirdPerson : WalkBase, IPerson
{
public override void Walk() => Console.WriteLine("BirdPerson.Walk()");
}
static void Main(string[] args)
{
BirdPerson birdPerson = new BirdPerson();
birdPerson.Walk(); // 输出 BirdPerson.Walk()
((WalkBase)birdPerson).Walk(); // 输出 BirdPerson.Walk()
((IPerson)birdPerson).Walk(); // 输出 BirdPerson.Walk()
((IBird)birdPerson).Walk(); // 输出 BirdPerson.Walk()
}
In the above example the last sentence candidates output, through IBird.Walk()
to find WalkBase.Walk()
and, WalkBase.Walk()
has found a method through the virtual link BirdPerson.Walk()
, the output still BirdPerson.Walk()
. C ++ students learned this time might very feeling!
As for Java, all methods are virtual methods. Although it can final
make it true, but not the same method signature defined in the sub-class, so the case of Java will be easier.
5.2. No class implementation, find the nearest default implementation according to the type of reference
Or take WalkBase
and BirdPerson
were achieved IBird
and IPerson
examples,
class WalkBase : IBird { }
class BirdPerson : WalkBase, IPerson { }
((IPerson)birdPerson).Walk(); // 输出 IPerson.Walk()
((IBird)birdPerson).Walk(); // 输出 IBird.Walk()
Oh, of course, does not exist in Java because the compiler would have to implement the requirements BirdPerson.Walk()
.
5.3. How well as more complex cases
Speak true, if there is really a more complex situation, I suggest doing the test now!
6. The default method with caution
Appears the default method has its historical reasons, in the design of a new library, it is best not to prematurely consider the default method for this problem. If there is a default behavior to be achieved, may still abstract base class more suitable for some.
However, if designed classes and interfaces relations are indeed very complex, requiring similar or even multiple inheritance relationship, then consider the appropriate default method is also not a bad idea.