Have a class Car
with a public method
public Car myself() {
return this;
}
Have a subclass Ferrari
, and a variable foo
that contains a Ferrari
object.
Finally,
Ferrari bar = foo.myself();
This will warn you, because the method myself()
returns a Car
object, rather than the expected Ferrari
.
Note: I know that the example is stupid because you'd just do bar = foo
. It's just an example.
Solutions:
myself()
method in Ferrari
.Car
object to a Ferrari
object when assigning bar
.Both solutions work and I am okay with that. However, the first one is undesirable when you have several subclasses of Car
. I feel that overriding a method over and over defeats the point of inheriting it. Next, regarding the second solution, casting is not pretty. It feels silly - if my variable is of type Ferrari
, shouldn't Java be able to implicitly cast it without warning me? After all, Java must know that the returned object can be casted to Ferrari
, no?
Is there another workaround? Just out of curiosity - I can live with casting stuff, telling Java what things are supposed to be...
This solution uses generics in a way that is used more often in the Java libraries.
It works and you don't have to cast the result every time nor override the myself
method in every subclass.
I believe that it is the only solution that doesn't require overriding or casting. It does require each subclass to use its own type as a type parameter to the superclass Car
: class Ferrari extends Car<Ferrari>
class Car<X extends Car<X>> {
public X myself() {
return (X) this;
}
}
class Ferrari extends Car<Ferrari> {
}
And then use it as you intended:
Ferrari testarossa = new Ferrari().myself();
This concept is used in the Java standard libraries a few times as well in one way or another:
java.lang.Enum
public abstract class Enum<E extends Enum<E>>
java.util.Comparable
public interface Comparable<T>
(You're supposed to pass your own class type when you implement a comparable: class ShoeSize implements Comparable<ShoeSize>
)
Method chaining
There's a good use for this too - there is a pattern, favored by some, that allows method chaining. This is what StringBuilder
does: new StringBuilder().append("a").append("b").toString()
. However a class that supports method chaining is often hard to subclass. Using the approach I outlined above makes it possible to subclass in this situation.