Why Is "Final" Not Allowed in Java 8 Interface Methods

Why is final not allowed in Java 8 interface methods?

This question is, to some degree, related to What is the reason why “synchronized” is not allowed in Java 8 interface methods?

The key thing to understand about default methods is that the primary design goal is interface evolution, not "turn interfaces into (mediocre) traits". While there's some overlap between the two, and we tried to be accommodating to the latter where it didn't get in the way of the former, these questions are best understood when viewed in this light. (Note too that class methods are going to be different from interface methods, no matter what the intent, by virtue of the fact that interface methods can be multiply inherited.)

The basic idea of a default method is: it is an interface method with a default implementation, and a derived class can provide a more specific implementation. And because the design center was interface evolution, it was a critical design goal that default methods be able to be added to interfaces after the fact in a source-compatible and binary-compatible manner.

The too-simple answer to "why not final default methods" is that then the body would then not simply be the default implementation, it would be the only implementation. While that's a little too simple an answer, it gives us a clue that the question is already heading in a questionable direction.

Another reason why final interface methods are questionable is that they create impossible problems for implementors. For example, suppose you have:

interface A { 
default void foo() { ... }
}

interface B {
}

class C implements A, B {
}

Here, everything is good; C inherits foo() from A. Now supposing B is changed to have a foo method, with a default:

interface B { 
default void foo() { ... }
}

Now, when we go to recompile C, the compiler will tell us that it doesn't know what behavior to inherit for foo(), so C has to override it (and could choose to delegate to A.super.foo() if it wanted to retain the same behavior.) But what if B had made its default final, and A is not under the control of the author of C? Now C is irretrievably broken; it can't compile without overriding foo(), but it can't override foo() if it was final in B.

This is just one example, but the point is that finality for methods is really a tool that makes more sense in the world of single-inheritance classes (generally which couple state to behavior), than to interfaces which merely contribute behavior and can be multiply inherited. It's too hard to reason about "what other interfaces might be mixed into the eventual implementor", and allowing an interface method to be final would likely cause these problems (and they would blow up not on the person who wrote the interface, but on the poor user who tries to implement it.)

Another reason to disallow them is that they wouldn't mean what you think they mean. A default implementation is only considered if the class (or its superclasses) don't provide a declaration (concrete or abstract) of the method. If a default method were final, but a superclass already implemented the method, the default would be ignored, which is probably not what the default author was expecting when declaring it final. (This inheritance behavior is a reflection of the design center for default methods -- interface evolution. It should be possible to add a default method (or a default implementation to an existing interface method) to existing interfaces that already have implementations, without changing the behavior of existing classes that implement the interface, guaranteeing that classes that already worked before default methods were added will work the same way in the presence of default methods.)

Why does Java 8 not allow non-public default methods?

As we saw in What is the reason why “synchronized” is not allowed in Java 8 interface methods? and Why is "final" not allowed in Java 8 interface methods?, extending interfaces to define behavior is more subtle than it might first appear. It turns out that each of the possible modifiers has their own story; its not simply a matter of blindly copying from how classes work. (This is at least obvious in hindsight, as tools for OO modeling that work for single inheritance do not automatically work for multiple inheritance.)

Let's start with the obvious answer: interfaces have always been restricted to only having public members, and while we added default methods and static methods to interfaces in Java 8, that doesn't mean we have to change everything just to be "more like" classes.

Unlike with synchronized and final, which would have been serious mistakes to support for default methods, weaker accessibilities, especially private, are reasonable features to consider. Private interface methods, whether static or instance (note that these would not be defaults, since they do not participate in inheritance) are a perfectly sensible tool (though they can be easily simulated by nonpublic helper classes.)

We actually did consider doing private interface methods in Java 8; this was mostly something that just fell off the bottom of the list due to resource and time constraints. It is quite possible this feature might reappear on the to-do list some day. (UPDATE: private methods in interfaces were added in Java 9.)

Package and protected methods, however, are more complicated than they look; the complexity of multiple inheritance and the complexity of the true meaning of protected would interact in all sorts of no-so-fun ways. So I wouldn't hold your breath for that.

So, the short answer is, private interface methods is something we could have done in 8, but we couldn't do everything that could have been done and still ship, so it was cut, but could come back.

default methods in interface but only static final fields

All fields in interfaces in Java are public static final.

Even after addition of default methods, it still does not make any sense to introduce mutable fields into the interfaces.

Default methods were added because of interface evolution reasons. You can add a new default method to the interface, but it only makes sense if the implementation uses already defined methods in the interface:

public interface DefaultMethods {

public int getValue();

public default int getValueIncremented() {
if (UtilityMethod.helper()) { // never executed, just to demonstrate possibilities
"string".charAt(0); // does nothing, just to show you can call instance methods
return 0;
}

return 1 + getValue();
}

public static class UtilityMethod {

public static boolean helper() {
return false;
}
}
}

Final arguments in interface methods - what's the point?

It doesn't seem like there's any point to it. According to the Java Language Specification 4.12.4:

Declaring a variable final can serve
as useful documentation that its value
will not change and can help avoid
programming errors.

However, a final modifier on a method parameter is not mentioned in the rules for matching signatures of overridden methods, and it has no effect on the caller, only within the body of an implementation. Also, as noted by Robin in a comment, the final modifier on a method parameter has no effect on the generated byte code. (This is not true for other uses of final.)

Why are interface variables static and final by default?

From the Java interface design FAQ by Philip Shaw:

Interface variables are static because Java interfaces cannot be instantiated in their own right; the value of the variable must be assigned in a static context in which no instance exists. The final modifier ensures the value assigned to the interface variable is a true constant that cannot be re-assigned by program code.

source

Why `private static` field is not allowed in Java 8 interface?

In the pre-Java-8 view of the world, interfaces were purely for interface contracts, and private members exist purely for implementation, so this restriction was completely sensible.

In the post-Java-8 world, where interfaces can carry behavior (but not state), it starts to be reasonable to ask whether other features of classes should be applied to interfaces as well. (However, just because something might be "reasonable" doesn't mean it must be supported; there is often more than one reasonable way to construct the world.)

In Java 9, private methods in interfaces will be supported.

Why is adding default methods to interfaces in Java 8 a good design choice and what are the alternatives

Why was this language feature introduced?

It was added primarily to let you add methods to existing interfaces that are already in use without breaking everyone's code, but also to share method implementations "horizontally" across classes implementing the same interface (as opposed to "vertical" sharing through inheritance).

What key new features does it support? (for instance Splititerators)

java.util.Collection<T>.stream()

What other alternatives were there to support those language features? For example, why not create a new interface SplitIterable that extends Iterable?

You could opt for entirely new interfaces, and continue with the associated static helper class (e.g. Collection<T> interface and its Collections helper class). This wouldn't be terrible - in fact, one could argue that default methods are purely a syntactic sugar on top of static methods*. However, default methods generally provide for better readability.

What would be the impact of implementing those alternatives (proliferation of interfaces?)

You would end up with a less consistent library, and a less readable language, but it wouldn't be the end of the world. A bigger concern, as pointed out by Joachim Sauer, is that interface implementations would have no way to override implementation from the static helper class. That would take away flexibility.

Should I provide a default implementation for a method in the first edition of interface when it is possible to implement it as a composition of other methods?

You should do it only if you need to share implementation "horizontally". If a method provides essential behavior of the implementation, do not provide a default for it.

* This would be an oversimplification, because default methods remain virtual. Thanks Brian Goetz for the comment.

Why doesn't Java 8 allow interface members to be private?

Because of time constraints in the implementation.

Private methods were originally within spec, but in an email titled "Some Pullbacks", sent by Brian Goetz to the lambda-spec-experts mailing list back when Java 8 was in development, they were pulled.

We would like to pull back two small features from the JSR-335 feature plan:

  • private methods in interfaces
  • "package modifier" for package-private visibility

The primary reason is resourcing ...



Related Topics



Leave a reply



Submit