What the hell is the default method Interface

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 IStringListan 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 IStringListafter 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.StringStackSortedStringListIStringList

Then IStringListthe inventor, decided to define a list of more ways to suit developers of the rapid development in technology IStringListrequires 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 AbstractStringListimplemented 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 IStringListchanges, users need to spend a lot of time to code to implement a IStringListnew 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 IStringListinterface 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);
}

MyListNot 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, MyListto 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 Mainthe 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 IndexOfmethod. The first call is IStringList.IndexOfthe default implementation, because at that time MyListdid not materialize IndexOf; the second call is MyList.IndexOfrealized. 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 MyListthe realized IndexOf, the result makes no difference. However, if MyListnot implemented in IndexOftime, Java and C # have on the difference in treatment.

Take a look at the C # Mainfunction, but the compiler ( Compiler Error CS1929 ), because MyListnot defined IndexOf.

What the hell is the default method Interface

The Java do? Passed, as always, running the results!

What the hell is the default method Interface

C # from the point of view, MyListsince we know that there is IndexOfan interface, it should realize it, but can not pretend not to know. But if by IStringListto call IndexOf, then it can be considered MyListnot aware of IndexOfthe 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, MyListthe consumer is not necessarily MyListthe producers. From a consumer point of view, MyListto achieve the StringListinterface, and there is the interface definition indexOfmethod, so consumers call myList.indexOfis 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 MyListto achieve the IndexOffollowing, such a call is switched directly to the calling MyListimplementations, 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 BirdPersonimplemented 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, Personand Birdare all the same signature walkdefines the default current method, the compiler does not know BirdPersonin the end how to do it. So if only one walkhas 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 BirdPersonhave to implement their own walk(). Since the BirdPersoninternal 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, WalkBasethe definition of a Walk()method, but did not implement any interfaces, BirdPersonfrom WalkBaseinheritance, implements IPersonthe interface, but did not realize Walk()method, then the implementation of which Walkit?

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 WalkBaseto virtualdefine Walk(), and BirdPersonto overridedefine 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 finalmake 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 WalkBaseand BirdPersonwere achieved IBirdand IPersonexamples,

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.

Guess you like

Origin blog.51cto.com/jamesfancy/2476640