Java's defects in Interface 2004-10-31

xiaoxiao2021-03-05  29

There is an important principle in OO design: the structure (Mechanism) is separated from the Policy. Interface is an important way to practice this principle.

From conceptual, Interface is used to play a contract between users and implementors. We use Interface to define a set of Abstract Operation (Method), implement these Interface with implementation classes, and the caller is accessed by accessing the corresponding services provided by these interface. The caller is not concerned with anyone who is implemented, and it is only the interface itself.

Contract | ----------- | Realize | ------------- | | Class A | --------> | Interface A | Access | ----- || ----------- | | ------------- | <------- | Caller | | OPERATION1 () | | - ------ || ----------- | Realize | Operation2 () || Class B | --------> | ------- ------ || ----------- |

Be sure to be based on the meaning of the above Interface when defining the interface specification. There is a defect in Java's Interface design.

In Java, a Class can inherit from another Class while can implement multiple interface. So, a Method can both inherit from the base class, or it can be implemented. Java's Method Signature consists of Method Name and Parameter List. This means that Java does not distinguish between Method and Interface implementation system from the Class inheritance system. This will cause a lot of confusion.

For example, we bought a Java package from Vendor A and Vendor B, which we may wish to call Package A and Package B. In Package A, there is a set of container inheritance

The system has a set of Container systems composed of IconTainer in Package B. As shown below:

Now we want to add a new container PowerVector, for it, we want to make it compatible with the Container architecture of Package A, and want it to be compatible with the Container system of Package B. So we let PowerVector inherit from the Vector class while letting it implement icontainer. As drawing in the figure.

This is a way to naturally. However, the problem is that PowerVector inherits the Size method from the vector (Container), while implementing the IContainer's size method. This two sets of architectures from two suppliers have exactly the same signature in Java, but it contains completely different meaning. In the Container system of Package A, the Size method is used to query the capacity of Container; in Package B, the Size method is used to calculate the number of Items stored by Container.

Using Java, due to the SIZE method's signature, in PowerVector, you only have two options, inherit or rewrites the size method of the output. If it is inherited, the result of the error is obtained by the size method of icontainer. If you rewrite to cater to IconTainer, you are equally erroneous when accessing the Size method through the Container system of Package A. You may say: This is very simple, can't you change the Size method of one of the systems? This is a suggestion without bra powrities. Think about it, because Package A and Package B come from two different vendors, they do not consider whether others are named when developing their respective packages. In your own system, there is already a large number of definitions using your own system. Modifying any one will be very troublesome. What's more, you are very likely that you have compiled package, and you will be changed.

If you are a Java master, you may be able to solve this problem with some special architectures or methods. But those methods are not the most natural way. A well-designed language should make designers or programmers to express their intentions with the most natural way.

The examples we give above are not a special situation. But a case that often appears. Only the Java design has such problems because Java language designers don't have deep understanding of OO thinking on this issue.

This is because, although Java only allows but inherits, it allows multiple interfaces to be implemented, then the Operation (Method) owned by a Class will come from multiple lines. And each line can be designed by different people, different companies, although the SUN is signed for Class or Interface, because SUN recommends the Package naming rules, you can avoid signing conflicts, but for Method's signature, follow Java Method Signature rules, the possibility of conflicting conflicts is large. And the same signature Method is likely to have a lot of differences.

From essentially, if a class has inherited another class, only one or more interface, or when multiple interfaces are implemented, if these interfaces and classes have the same signature Method (in accordance with Java signature principles These Method should be seen as Method with different signatures. It should be seen separately. such as:

// The following code is not Java Code nor C Code, but a pseudo-codepublic class base {public int foo () {return 0;}}

Public interface ifoo {int foo ();

Public class deive extends base imports ifoo {// There is also a foo method Int iFoo :: foo () {returno 10;} // need to indicate this is the implementation of IFOO's foo motor}

This way, if we have such a Code,

Derived; derived.foo (); // call is Base :: fooifoo ifoo = (ifoo) Derived; ifoo.foo (); // call is Base ::10

Another example: public interface ifoo1 {int foo ();

Public interface ifoo2 {int foo ();

Public class foo imports ifoo1, ifoo2 {// Need to indicate IFOO1 and IFOO2 Foo Mothed Implement INT IFOO1 :: Foo () {Return 0;} int if}} int}

Foo obj = new foo; ifoo1 foo1 = (ifo1) Obj; foo1.foo (); // calling foo ::10O1 :: fooifoo2 foo2 = (iFoo1) obj; foo2.foo (); // call is Foo :: ifoo2 :: foo

For such a solution, a problem that is generated is that if we implemented an interface in a Class, how do we access these Interface Methods when we use the Class's INSTANCE?

My answer is: I don't allow access because these methods are because this Class implements the interface where the method is located, and the implementation of these methods defines the behavior of the contract, that is, they are part of the contract being executed. Since the interface is a contract between the caller and service provider, the way to access them must of course pass through the interface. This is the semantics of Interface. If you really want to access these Interface Method, you can pack them to Class Method.

Since the interface allows an extension to expand other interfaces, this will result in such a problem:

Public interface ifoobase {int foo ();

Public interface ifoo1 extends ifoobase {void Draw ();

Public interface ifoo2 extends ifoobase {void move ();

Public class foo imports ifoo1, ifoo2 {...}

In this case, both IFOO1 and IFOO2 inherit the FOO method from Ifoobase, if we implement these two FOOs in Class Foo, there is a problem, such as:

Foo Obj = new foo; ifoobase foo0 = (ifoo1) Obj; foo0.foo (); // ??? Which implementation is in the end? ?

Therefore, for this situation, we must specify that if two or more interfaces extend an interface, and then these interfaces are implemented by the same class, you cannot achieve it, but to specify Base directly. Interface method is implemented. The above example is example, and its implementation should be:

Public class foo imports ifoo1, ifoo2 {int ifoobase :: foo () {Return 0;} void iFoo1 :: draw () {...} void iFoo2 :: move () {...}}

For this situation, there is also a form of expression:

Public class base imports ifoo1 {ion ifoo1 :: foo () {return 0;} // Here we only need to specify ifoo1 can void ifoo1 :: draw () {...}}

Public class deived extends base imports ifoo2 {void iFoo2 :: move () {...} // For iFOO2 :: foo (), because we can inherit its implementation, we can override it. / / What is we use when rewritten? // ifoo1 :: foo ()? Ifoo2 :: foo ()? Or ifoobase :: foo ()} This situation, random specified scope will cause a lot of confusion. In order to avoid this problem, we can make such a provision: When we are implementing an interface, we specify its interface, rather than the extended Interface. According to this regulations, our implementation should be:

Public class base imports ifoo1 {int ifoobase :: foo () {returno 0;} // Here we must specify ifoobase void ifoo1 :: draw () {...}}

Public class deived extends base imports ifoo2 {void ifoo2 :: move () {...} // For ifoobase :: foo (), because we have implemented in Base, // We can inherit its implementation, you can also override it. / / But we still have to use ifoobase :: foo ()} when rewritten.

All of the above form a complete interface solution, in accordance with this scheme, designer and programmer can naturally, intuitively, and correctly use the interface for OO architectural design. It also expects Java to solve this defect in the future version.

转载请注明原文地址:https://www.9cbs.com/read-35928.html

New Post(0)