Allow interface default toString implementation
Brandon Mintern
mintern at everlaw.com
Tue Jun 10 22:43:08 UTC 2014
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