Default methods were introduced to provide backward compatibility for old interfaces so that they can have new methods without affecting existing code.
In short: Default methods should not be needed. The only reason they where introduced and are used within the JDK is to stay downwards compatible, and this should be the only reason to use it in your library.
Since Java8 static methods and default methods are introduced in interfaces. Unlike other abstract methods these are the methods can have a default implementation. If you have default method in an interface, it is not mandatory to override (provide body) it in the classes that are already implementing this interface.
Default methods enable you to add new functionality to existing interfaces and ensure binary compatibility with code written for older versions of those interfaces. In particular, default methods enable you to add methods that accept lambda expressions as parameters to existing interfaces.
This is yet another of those language design issues that seems "obviously a good idea" until you start digging and you realize that its actually a bad idea.
This mail has a lot on the subject (and on other subjects too.) There were several design forces that converged to bring us to the current design:
AbstractList
into an interface), you realize that inheriting equals/hashCode/toString is strongly tied to single inheritance and state, and interfaces are multiply inherited and stateless;You've already touched on the "keep it simple" goal; the inheritance and conflict-resolution rules are designed to be very simple (classes win over interfaces, derived interfaces win over superinterfaces, and any other conflicts are resolved by the implementing class.) Of course these rules could be tweaked to make an exception, but I think you'll find when you start pulling on that string, that the incremental complexity is not as small as you might think.
Of course, there's some degree of benefit that would justify more complexity, but in this case it's not there. The methods we're talking about here are equals, hashCode, and toString. These methods are all intrinsically about object state, and it is the class that owns the state, not the interface, who is in the best position to determine what equality means for that class (especially as the contract for equality is quite strong; see Effective Java for some surprising consequences); interface writers are just too far removed.
It's easy to pull out the AbstractList
example; it would be lovely if we could get rid of AbstractList
and put the behavior into the List
interface. But once you move beyond this obvious example, there are not many other good examples to be found. At root, AbstractList
is designed for single inheritance. But interfaces must be designed for multiple inheritance.
Further, imagine you are writing this class:
class Foo implements com.libraryA.Bar, com.libraryB.Moo {
// Implementation of Foo, that does NOT override equals
}
The Foo
writer looks at the supertypes, sees no implementation of equals, and concludes that to get reference equality, all he need do is inherit equals from Object
. Then, next week, the library maintainer for Bar "helpfully" adds a default equals
implementation. Ooops! Now the semantics of Foo
have been broken by an interface in another maintenance domain "helpfully" adding a default for a common method.
Defaults are supposed to be defaults. Adding a default to an interface where there was none (anywhere in the hierarchy) should not affect the semantics of concrete implementing classes. But if defaults could "override" Object methods, that wouldn't be true.
So, while it seems like a harmless feature, it is in fact quite harmful: it adds a lot of complexity for little incremental expressivity, and it makes it far too easy for well-intentioned, harmless-looking changes to separately compiled interfaces to undermine the intended semantics of implementing classes.
It is forbidden to define default methods in interfaces for methods in java.lang.Object
, since the default methods would never be "reachable".
Default interface methods can be overwritten in classes implementing the interface and the class implementation of the method has a higher precedence than the interface implementation, even if the method is implemented in a superclass. Since all classes inherit from java.lang.Object
, the methods in java.lang.Object
would have precedence over the default method in the interface and be invoked instead.
Brian Goetz from Oracle provides a few more details on the design decision in this mailing list post.
I do not see into the head of Java language authors, so we may only guess. But I see many reasons and agree with them absolutely in this issue.
The main reason for introducing default methods is to be able to add new methods to interfaces without breaking the backward compatibility of older implementations. The default methods may also be used to provide "convenience" methods without the necessity to define them in each of the implementing classes.
None of these applies to toString and other methods of Object. Simply put, default methods were designed to provide the default behavior where there is no other definition. Not to provide implementations that will "compete" with other existing implementations.
The "base class always wins" rule has its solid reasons, too. It is supposed that classes define real implementations, while interfaces define default implementations, which are somewhat weaker.
Also, introducing ANY exceptions to general rules cause unnecessary complexity and raise other questions. Object is (more or less) a class as any other, so why should it have different behaviour?
All and all, the solution you propose would probably bring more cons than pros.
To give a very pedantic answer, it is only forbidden to define a default
method for a public method from java.lang.Object
. There are 11 methods to consider, which can be categorized in three ways to answer this question.
Object
methods cannot have default
methods because they are final
and cannot be overridden at all: getClass()
, notify()
, notifyAll()
, wait()
, wait(long)
, and wait(long, int)
.Object
methods cannot have default
methods for the reasons given above by Brian Goetz: equals(Object)
, hashCode()
, and toString()
.Two of the Object
methods can have default
methods, though the value of such defaults is questionable at best: clone()
and finalize()
.
public class Main {
public static void main(String... args) {
new FOO().clone();
new FOO().finalize();
}
interface ClonerFinalizer {
default Object clone() {System.out.println("default clone"); return this;}
default void finalize() {System.out.println("default finalize");}
}
static class FOO implements ClonerFinalizer {
@Override
public Object clone() {
return ClonerFinalizer.super.clone();
}
@Override
public void finalize() {
ClonerFinalizer.super.finalize();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With