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