Is there a way to provide a default toString() to an interface?
Brian Goetz
brian.goetz at oracle.com
Wed Mar 12 15:57:41 UTC 2014
This is a common question. The short answer is "no".
The short explanation of "why not", is that if there's a conflict
between a superCLASS method and a superINTERFACE method, the class
method wins. Since Object is a class, the toString (or hashCode, or
equals) in Object will win over any default. (The error is there to
call your attention to the fact that this is a method that cannot be
inherited by any subtype; without the error, you'd be posting an even
more annoyed message when you discovered (probably much later) that the
implementation was not being inherited.)
But of course you can ask "well, why is this the rule?" This rule
wasn't chosen arbitrarily; alternate rulesets were investigated, some of
which might have enabled this usage pattern. But they were dramatically
more complicated, and had poorer compatibility characteristics, for very
little incremental expressiveness. This current ruleset is simple and
100% compatible (any method inherited under 7 works exactly the same way
under 8.)
Looking at it from the other side, I understand why one would want to
write a default method for equals/hashCode/toString, but in reality this
is a pretty bad idea. The reason is that these methods are inextricably
bound to an object's state. And the state lives in classes, not
interfaces; the class should be the one providing these methods. If we
allowed you to inherit these methods from an interface, it would still
only work predictably for inheritance patterns that are strictly
single-inheritance, such as if we wrote AbstractList as an interface
(which we could, because it has no state). But in the general case,
interfaces are not used that way, and the weirdness that would ensue
because some library class three libraries and nine levels of
inheritance up added an equals method and that was inherited by your
application and subtly changed its behavior would be a nightmare.
So, summarizing:
- It would dramatically complicate inheritance rules to enable this, and
- Its a bad idea anyway.
Now, if you have a complicated toString() method you want to put in an
interface, you can do this:
interface Wrapper {
default String wrapperAsString() { ... }
}
class X implements Wrapper {
String toString() { return wrapperAsString(); }
}
But the class has to opt-into this being the toString implementation.
Same with equals/hashCode.
On 3/12/2014 3:45 AM, Wang Weijun wrote:
> Hi All
>
> I have an interface that wraps an integer, like this
>
> interface Wrapper {
> int getX();
> }
>
> Why cannot I add a default toString method
>
> default String toString() {
> return "This is " + getX();
> }
>
> The error is
>
> error: default method toString in interface DSAPublicKey overrides a member of java.lang.Object
> default String toString() {
>
> Is there any other way to do it? I do not want to rename it to getString() and let all child classes call it in their toString().
>
> Thanks
> Max
>
> P.S. security-dev@ guys, I want to move toString() back to {DSA|RSA|EC}PublicKey so that we get the same strings for SunPKCS11 and SunJCE.
>
More information about the security-dev
mailing list