From mintern at everlaw.com Tue Jun 10 22:43:08 2014 From: mintern at everlaw.com (Brandon Mintern) Date: Tue, 10 Jun 2014 15:43:08 -0700 Subject: Allow interface default toString implementation Message-ID: 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 From zhong.j.yu at gmail.com Wed Jun 11 01:12:12 2014 From: zhong.j.yu at gmail.com (Zhong Yu) Date: Tue, 10 Jun 2014 20:12:12 -0500 Subject: Allow interface default toString implementation In-Reply-To: References: Message-ID: On Tue, Jun 10, 2014 at 5:43 PM, Brandon Mintern wrote: > public class Object implements Objects { `Object` is supposed to be the root type of everything. It's too odd if it has a super type. Even if we do want to allow `toString()` default impl on interfaces, it'll be much easier to simply special-case it, instead of going through this exercise. However, the crux of the matter, I think, is to avoid depending on `toString()`. Zhong Yu From brian.goetz at oracle.com Wed Jun 11 15:57:06 2014 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 11 Jun 2014 11:57:06 -0400 Subject: Allow interface default toString implementation In-Reply-To: References: Message-ID: <53987C52.9070800@oracle.com> 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 > From mintern at everlaw.com Wed Jun 11 23:56:41 2014 From: mintern at everlaw.com (Brandon Mintern) Date: Wed, 11 Jun 2014 16:56:41 -0700 Subject: Allow interface default toString implementation In-Reply-To: References: Message-ID: On Tue, Jun 10, 2014 at 6:12 PM, Zhong Yu wrote: > On Tue, Jun 10, 2014 at 5:43 PM, Brandon Mintern > wrote: > > > public class Object implements Objects { > > `Object` is supposed to be the root type of everything. It's too odd > if it has a super type. > > Even if we do want to allow `toString()` default impl on interfaces, > it'll be much easier to simply special-case it, instead of going > through this exercise. > Fair enough. I would argue that the opposite is weird, that an interface can make use of class (rather than interface) methods like getClass() in default implementations. It's inconsistent with the rest of the language, where interface methods can never use class methods. That said, this is a problem only in principle, not in practice, so I don't have any qualms about your suggestion. However, the crux of the matter, I think, is to avoid depending on > `toString()`. I hope this isn't a serious suggestion. We don't rely on `toString()` directly -- this is the entire point of creating our own interface method like `toJson()` instead of assuming a class will implement `toString()` to do what we'd like. But live debuggers, logging systems, even Java's built-in String formatting and concatenation use `toString`, so it's not like it can just be avoided. From mintern at everlaw.com Thu Jun 12 00:09:25 2014 From: mintern at everlaw.com (Brandon Mintern) Date: Wed, 11 Jun 2014 17:09:25 -0700 Subject: Allow interface default toString implementation In-Reply-To: <53987C52.9070800@oracle.com> References: <53987C52.9070800@oracle.com> Message-ID: On Wed, Jun 11, 2014 at 8:57 AM, Brian Goetz wrote: > 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 > To be fair, that was me. It seemed like a discussion more appropriate for the mailing list, so I moved it here. 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." > Well now you're putting words into my mouth. I didn't say anything like "outright dangerous." I just noticed that the strongest argument in the original discussion was against hashCode/equals, and I'm happy to avoid arguing a point that has been clearly decided. I was hoping that I might be able to give a good case for treating toString differently. 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. > When you say special-purpose, are you referring to the idea of a toplevel interface, or something else? 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. > I'm sorry, but I don't understand the animosity here. I was trying to open up a discussion, and point out a specific use case that I thought illustrated the point of my suggestion reasonably well. I'm not trying to look for the solution to all of my problems. This is a much colder reception than I expected; I'm not trying to step on anyone's toes here. > So far, the simple rule is looking pretty good to me. What simple rule would that be? If something like this is not open to discussion, then why is this an open mailing list that is still accepting new subscribers? As an interested community member, one who has been following Java 8 developments for a year and is driving adoption at my company, it's pretty disheartening. You may as well take the discussion behind closed doors and stick to press releases. 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 >> >> From brian.goetz at oracle.com Thu Jun 12 03:47:35 2014 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 11 Jun 2014 23:47:35 -0400 Subject: Allow interface default toString implementation In-Reply-To: References: <53987C52.9070800@oracle.com> Message-ID: <472C9F08-45B0-4629-87B5-BF5353F2A4D0@oracle.com> > 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." > > Well now you're putting words into my mouth. I didn't say anything like "outright dangerous." I just noticed that the strongest argument in the original discussion was against hashCode/equals, and I'm happy to avoid arguing a point that has been clearly decided. I was hoping that I might be able to give a good case for treating toString differently. Putting words in my own mouth: I said ?dangerous?, and you said something like ?OK, I buy that for equals/hashCode, but what about 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. > > When you say special-purpose, are you referring to the idea of a toplevel interface, or something else? Changing the inheritance semantics to govern the inheritance of a single (admittedly important) method. Recall the discussion started on the topic of ?why can?t we inherit Object methods from interfaces?, and the argument against was basically ?changing the inheritance semantics to support that would be complex, for not a lot of value, and in some cases for negative value.? When hashCode and equals dropped out of the discussion, that leads to all the same complexity and more (because now it only applies to some methods), for even less value. You may well be thinking ?but this doesn?t require a language change?, because of the trick you suggest with an Objects interface. Its a clever trick, except that it doesn?t actually work ? its not behaviorally compatible. (Think about programs that use Object.class.getDeclaredMethod(); their behavior would change.) So really, we are talking about a language change, to support a single method (and IMO not even supporting it very well.) > > 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. > > I'm sorry, but I don't understand the animosity here. I was trying to open up a discussion, and point out a specific use case that I thought illustrated the point of my suggestion reasonably well. I'm not trying to look for the solution to all of my problems. This is a much colder reception than I expected; I'm not trying to step on anyone's toes here. > > So far, the simple rule is looking pretty good to me. > > What simple rule would that be? Method declarations in classes take precedence over default declared in interfaces (i.e., ?class wins?). In any case, bear in mind that the Java 8 ship has sailed, and switching the inheritance semantics now would change the behavior of existing code. From forax at univ-mlv.fr Thu Jun 12 07:38:02 2014 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 12 Jun 2014 09:38:02 +0200 Subject: Allow interface default toString implementation In-Reply-To: References: <53987C52.9070800@oracle.com> Message-ID: <539958DA.9030900@univ-mlv.fr> On 06/12/2014 02:09 AM, Brandon Mintern wrote: > On Wed, Jun 11, 2014 at 8:57 AM, Brian Goetz wrote: > >> 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 >> > To be fair, that was me. It seemed like a discussion more appropriate for > the mailing list, so I moved it here. > > 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." >> > Well now you're putting words into my mouth. I didn't say anything like > "outright dangerous." I just noticed that the strongest argument in the > original discussion was against hashCode/equals, and I'm happy to avoid > arguing a point that has been clearly decided. I was hoping that I might be > able to give a good case for treating toString differently. [Let pretend Java 8 was not shipped ... ] Please don't forget that default methods are not only a Java the language feature, it's a VM feature that is used by Java the language. So what you are talking about here is to have a special case in the way the VM treats default methods. Adding a special case in the VM is something that have a higher cost that just having a special case in the compiler. As an example, you can take a look to the long trail of bugs generated by the fact that there is a special case in the VM spec for Object::clone in case of arrays (instead of being protected, the method is public). https://bugs.openjdk.java.net/issues/?jql=text%20~%20%22clone%20array%22 And to finish, Java is not the only language that run on the JVM, so adding a special case to the VM spec has the nasty side effect to add a special case to every languages than run on the JVM. regards, R?mi