The Java language interface is a powerful tool. It has many advantages of multiple inheritance, without any problems. Specify an interface for all the services that the customer wants to use, making it possible to insert different implementations of this interface when needed.
Unfortunately, the part of the specification that can be expressed is only a description of the method. For any implementation, there are probably many other invariants that want to be mastered, but the Java language does not provide the tools to check them.
Imaginary error pattern
Because of this limitation, it is possible to "implement" an interface without actually satisfying the intended semantics. The error caused by this fictitious implementation is the theme of this week's column.
For example, take a look at the interface of the stack below:
Listing 1. The interface of the stack
public interface Stack {
public Object pop();
public void push(Object top);
public boolean isEmpty();
}
From the point of view of the Java type Checker, any class that contains a method that conforms to the instructions described above can be used as a legitimate implementation of Stack. But in fact, we want the stack to meet some additional requirements. For example:
If an object o is pressed into the stack s, and the next action on the stack is pop, the return value of the operation should be O.
If the return value of S.isempty () is true for a given stack s, and the next action on this stack is pop, then the calling pop should throw a RuntimeException exception.
There are also a number of other invariants that can be specified. What do we want the stack to do with multiple push operations? What is the behavior for multithreading? It is difficult to programmatically implement these invariants. We can (and should) mention them when the document is written, but developers who write implementations can easily ignore them. If this happens, customers who rely on these invariants will not be able to complete this implementation, creating an error. I call this pattern a mistake for fictitious implementation, because I justly blame it on the implementation rather than the client. Just as any error has its own pattern, fictitious implementation may not immediately see it, but lurks it, hiding it into an unusual execution path to find it.
Don't blame the Java language!
Before continuing this column, I would like to point out that I am not criticizing the Java language for not specifying such invariants. Any mechanism that allows this specification will have a number of attendant drawbacks. First, we want to specify a lot of invariants that cannot be checked statically. Although the type description only expresses a small portion of the invariant, it is easier to check than the one we outlined above for this type of constraint for the stack.
The other disadvantage of allowing more expressive specifications in an interface is that it makes it easy for the Java language to carry a lot of problems, so that there are multiple inheritance everywhere in the language. Please look at the following interface:
Listing 2. Popup connector
public interface Popper {
public Object pop();
}