Per Effective Java by Joshua Blotch:
There is no way to extend an instantiable class with a new value component while preserving the
compareTo
contract, unless you are willing to forgo the benefits of object-oriented abstraction
Can you please explain the above with examples and the challenges? Can you also explain what Joshua means by "Value Component" and what other types of components are available.
This frees you to implement whatever
compareTo
method you like on the second class, while allowing its client to view an instance of the second class as an instance of the first class when needed.
Can you also explain what Joshua means by second class as an instance of the first class?
Can you please explain the above with examples and the challenges?
Sure. Consider two classes like this - I've left out all the getters, setters etc, but hopefully you get the drift:
class NamedThing {
String name;
}
class Person extends NamedThing {
Date dateOfBirth;
}
Ignore whether this is a good example of inheritance - it's a simple one.
It would be natural for NamedThing
to implement a comparison based on name, in alphabetical order.
It would also be natural for Person
to implement a comparison which first compares the name (so stays consistent in that respect) but then also checks for one date of birth being earlier than another.
Now imagine this:
NamedThing person1 = new Person("Jon", date1);
NamedThing person2 = new Person("Jon", date2);
NamedThing thing = new NamedThing("Jon");
int comparison1 = person1.compareTo(thing);
int comparison2 = person2.compareTo(thing);
int comparison3 = person1.compareTo(person2);
int comparison4 = thing.compareTo(person1);
What would you want all these results to be? If Person.compareTo
was smart enough to only apply its date processing to instances of Person
, then you might expect comparison1
and comparison2
to be 0, but comparison3
to be non-zero.
Presumably comparison4
would have to be 0, as it's using NamedThing.compareTo
, which only compares names.
Fundamentally, there's a problem trying to compare instances of different types. It ends up being cleaner to have an external definition of a comparison, which defines what comparison it will use. You could thus have a Comparator<Person>
which only accepted Person
references and used both name and date, and a Comparator<NamedThing>
which only compared by name. The behaviour would have symmetry and clarity.
Can you also explain what Joshua means by second class as an instance of the first class?
You've taken it out of context. It's: "view an instance of the second class as an instance of the first class" - e.g.
// First class = Animal
// Second class = Zebra
Animal person = new Zebra();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With