I was wondering, does anyone know if there's a difference between the following two declarations?
Both seem to compile and run fine. But I was wondering, is the 2nd more semantically correct than the 1st?
And what am I trying to do here, I hear you ask? I'm trying to create various types of fruits - Apples, Oranges - that can only compare with themselves. The comparison logic is the same for all fruits, so I'm placing that logic in the superclass Fruit and using generics to bind compareTo() to the correct subclass. This avoids duplicating the same comparison code in all subclasses. The rest of the code looks like this (compiles and runs correctly):
I'm using the same Apple and Orange definitions for both types of Fruit header declarations.
Since Apple extends Fruit<Apple> and Orange extends Fruit<Orange>, I thought the 2nd Fruit declaration (using <E extends Fruit<E>> would be the correct one. However, the code compiles and runs fine with the 1st Fruit declaration too (using <E extends Fruit> , so that got me wondering if there really is any difference between the two.
All comments are welcome, cheers!
PS. Yes, for those eagle eyes, this is a modified example from Naftalin and Wadler's "Java Generics and Collections", combined with ideas from Java 5's Enum implementation :-)
King Lung Chiu
Joined: Feb 16, 2008
... and please ignore those winking icons - they're supposed to be closing brackets - not sure how they got there ...
Why are you parameterising Fruit in the first place? I can see no benefit in writing
(1) I only want a specific Fruit subclass to be comparable to themselves, and not to any other different subclass. eg. I don't want to allow an Apple to be comparable to an orange.
(2) I don't want to duplicate the compareTo() code in all Fruit subclasses, so I'm writing it once only in the Fruit superclass, and using generics to bind it to a specific subclass when required. Please look at the compareTo() definition in my first post again, it'll make more sense.
And surely it's
Yes, if you want to use compareTo(Fruit), ie. allow all Fruits to be comparable to each other, even allow an Apple to be comparable to an Orange.
No, if you don't want to use compareTo(Fruit), but rather compareTo(A-Specific-Fruit-Subclass), ie. only allow a Fruit subclass to be compared to the same Fruit subclass.
And I'm trying to design for the 2nd case.
I appreciate the alternative suggestions, but I've already considered them and they're not suitable for what I need. So please assume that I have good reasons to use Apple extends Fruit<Apple> and [I]Orange extends Fruit<Orange>[I], and that given such case, whether there is any technical or semantic differences between the following Fruit header declarations (both have the exact same class bodies, and both compile and run fine with the same Apple and Orange definitions show above):
I'm no expert in generics, but both seem to be equivalent. And they both fail in some interesting ways.
[ February 17, 2008: Message edited by: Garrett Rowe ]
Some problems are so complex that you have to be highly intelligent and well informed just to be undecided about them. - Laurence J. Peter
King Lung Chiu
Joined: Feb 16, 2008
Thanks a lot for the reply. And thanks for the interesting examples.
I think I can explain why they're like that (my understanding):
Here, f1 is Fruit<Apple>, so its compareTo() takes an Apple, thus it compiles fine.
Here, since f1 is a Fruit<Apple> rather than an Apple, and since a1.compareTo() takes an Apple, compilation fails. Though it's safe to cast f1 back into an Apple, if required, since it references an Apple.
Here, both f1 and f2 are Fruit<Apple>s, and because of the parameterisation, they both use compareTo(Apple), rather than compareTo(Fruit<Apple>), thus the compilation fails.
And I think you're right that Fruit<E extends Fruit> and Fruit<E extends Fruit<E>> are equivalent ... at least according to the 1.6.0_01 compiler I'm using, they both give the same errors with your examples.