Allow interface default toString implementation

Brian Goetz brian.goetz at oracle.com
Wed Jun 11 15:57:06 UTC 2014


This recently came up on StackOverflow too:
 
http://stackoverflow.com/questions/24016962/java8-why-is-it-forbidden-to-define-a-default-method-for-a-method-from-java-lan/24026292#24026292

What you're saying is "OK, I accept that its outright dangerous to 
inherit hashCode and equals from interfaces, but we should do something 
different for toString."

So this would be, basically, designing a special-purpose language 
feature for giving special inheritance behavior to one method.  Seems a 
poor return-on-complexity.

Additionally, while this approach might magically make toString 
inheritance work well in *your* library, the reality is that toString 
shares the essential feature with equals and toString that it is 
primarily about state, and state resides with the class, not the 
interface.  You happen to have built a library where you've sufficiently 
virtualized the state, but your library is the exception, not the rule. 
  Making for an even worse return-on-complexity.

So far, the simple rule is looking pretty good to me.

On 6/10/2014 6:43 PM, Brandon Mintern wrote:
> I know that I'm late to the party, but I want to revisit an idea that was
> proposed over a year ago:
>
> http://mail.openjdk.java.net/pipermail/lambda-dev/2013-March/008435.html
>
> I read the thread and I appreciate the concerns in regards to
> equals/hashCode. I sympathize especially with Brian's 4th point:
>
> It's brittle.  Methods like equals are really fundamental; you don't
>> want a classes equals() behavior changing out from under you when a
>> library is rev'ed and someone adds an equals() implementation to some
>> interface that you indirectly inherit from nine levels away.  But this
>> is exactly what would happen if someone added an equals() method to an
>> existing interface, if its subtypes didn't provide their own equals().
>
>
> As I begin converting our codebase to take advantage of JDK 8's excellent
> features, the one I miss most so far is the ability to provide an interface
> default implementation of toString.
>
> For a concrete example, we use a JSON-formatted Lisp-like query language
> for converting a complicated frontend search across various properties of
> searchable objects into a backend tree structure that eventually performs
> the search in Lucene, SQL, etc.
>
> Each unique searchable property has a concrete class that specifies things
> such as where and how to search for the property. Of course, there is a
> toplevel interface -- called Property -- that defines the core
> functionality. The relevant method for this discussion is String toJson();
> we require that every property can be converted back to its JSON
> representation.
>
> Of course, now that we have a meaningful String representation of each
> object, we should use it for toString(). In fact, it would be extremely
> convenient to be able to add this to Property:
>
>      @Override
>      default String toString() {
>          return toJson();
>      }
>
> Our alternatives, instead, are to either add the toString method to every
> single property, or to create some base class:
>
> abstract class PropertyToString implements Property {
>      @Override
>      public String toString() {
>          return toJson();
>      }
> }
>
> We opted to go this PropertyToString route, as the boilerplate of adding
> those 4 lines to every single property was a bit painful. This is an ugly
> solution, though, as some of the properties have other base classes; this
> abstract class has to be injected into the top of each such mini-hierarchy,
> and now we're changing our class hierarchy just to get some
> debugging/logging conveniences.
>
> An alternative, which I think is still possible going forward, is to move
> toString() out of Object so that it *can* be a default method. I realize
> that it makes use of getClass and hashCode, so it would need to involve a
> hierarchy like this:
>
> public interface Objects {
>      ...
>      Class<?> getClass();
>      int hashCode();
>      boolean equals(Object obj);
>      default String toString() {
>          return getClass().getName() + "@" + Integer.toHexString(hashCode());
>      }
>      ...
> }
>
> public class Object implements Objects {
>      ...
>      @Override
>      public final native Class<?> getClass();
>      @Override
>      public native int hashCode();
>      @Override
>      public boolean equals(Object obj) {
>          return (this == obj);
>      }
>      ...
> }
>
> All interfaces would implicitly extend Objects. Because Object overrides
> hashCode and equals, default interface implementations of those methods
> would be moot. But because Object does not override toString(), it gives
> interfaces a chance to provide a default implementation in those cases
> where it makes sense to do so.
>
> Was there any discussion of an approach like this?
>
> Thanks,
> Brandon
>


More information about the lambda-dev mailing list