Regarding Java subclasses inheriting methods that return “this”

僤鯓⒐⒋嵵緔 提交于 2019-12-24 03:37:10

问题


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:

  • Override the myself() method in Ferrari.
  • Cast the 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...


回答1:


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.




回答2:


This also depends on how you know the foo variable.

If you know it by

Ferrari foo = new Ferrari();

then an overridden method like

class Ferrari extends Car {
    @Override
    public Ferrari myself() { 
        return this;
    }
}

will allow you to exploit the covaraince, and you can do

Ferrari foo = new Ferrari();
Ferrari bar = foo.myself();

although there are probably not many application cases where this makes sense: When you already know that it is a Ferrari, you could just write

Ferrari bar = foo;

The situation is different when you only know it by

Car foo = new Ferrari();

Then you're out of luck: You don't know the runtime type any more, and you'll have to cast anyhow.

(Note: Such an overridden method returning this with a more specific type has a real application case when it comes to self-referential generic types. This is sometimes referred to as the getThis trick)

Concerning your statement

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?

This is not true. No one prevents you from overriding the method like

class Ferrari extends Car {
    @Override
    public Car myself() { 
        return new Volkswagen(); 
    }
}

and then the cast would no longer be valid




回答3:


A sub-class may override a base-classes method, and as part of that contract indicate that it will return the specific type. This provides clarity to the user of the class, whereas expecting the user to know the type being returned and cast it accordingly does not. Therefore, overriding is better.



来源:https://stackoverflow.com/questions/23041693/regarding-java-subclasses-inheriting-methods-that-return-this

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!