How do I make the method return type generic?
Consider this example (typical in OOP books):
I have an Animal
class, where each Animal
can have many friends.
And subclasses like Dog
, Duck
, Mouse
etc which add specific behavior like bark()
, quack()
etc.
Here's the Animal
class:
public class Animal {
private Map<String,Animal> friends = new HashMap<>();
public void addFriend(String name, Animal animal){
friends.put(name,animal);
}
public Animal callFriend(String name){
return friends.get(name);
}
}
And here's some code snippet with lots of typecasting:
Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());
((Dog) jerry.callFriend("spike")).bark();
((Duck) jerry.callFriend("quacker")).quack();
Is there any way I can use generics for the return type to get rid of the typecasting, so that I can say
jerry.callFriend("spike").bark();
jerry.callFriend("quacker").quack();
Here's some initial code with return type conveyed to the method as a parameter that's never used.
public<T extends Animal> T callFriend(String name, T unusedTypeObj){
return (T)friends.get(name);
}
Is there a way to figure out the return type at runtime without the extra parameter using instanceof
? Or at least by passing a class of the type instead of a dummy instance.
I understand generics are for compile time type-checking, but is there a workaround for this?
You could define callFriend
this way:
public <T extends Animal> T callFriend(String name, Class<T> type) {
return type.cast(friends.get(name));
}
Then call it as such:
jerry.callFriend("spike", Dog.class).bark();
jerry.callFriend("quacker", Duck.class).quack();
This code has the benefit of not generating any compiler warnings. Of course this is really just an updated version of casting from the pre-generic days and doesn't add any additional safety.
No. The compiler can't know what type jerry.callFriend("spike")
would return. Also, your implementation just hides the cast in the method without any additional type safety. Consider this:
jerry.addFriend("quaker", new Duck());
jerry.callFriend("quaker", /* unused */ new Dog()); // dies with illegal cast
In this specific case, creating an abstract talk()
method and overriding it appropriately in the subclasses would serve you much better:
Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());
jerry.callFriend("spike").talk();
jerry.callFriend("quacker").talk();
You could implement it like this:
@SuppressWarnings("unchecked")
public <T extends Animal> T callFriend(String name) {
return (T)friends.get(name);
}
(Yes, this is legal code; see Java Generics: Generic type defined as return type only.)
The return type will be inferred from the caller. However, note the @SuppressWarnings
annotation: that tells you that this code isn't typesafe . You have to verify it yourself, or you could get ClassCastExceptions
at runtime.
Unfortunately, the way you're using it (without assigning the return value to a temporary variable), the only way to make the compiler happy is to call it like this:
jerry.<Dog>callFriend("spike").bark();
While this may be a little nicer than casting, you are probably better off giving the Animal
class an abstract talk()
method, as David Schmitt said.
上一篇: Javadoc评论中有多行代码示例
下一篇: 我如何使方法返回类型通用?