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?
Generic MethodsAll generic method declarations have a type parameter section delimited by angle brackets (< and >) that precedes the method's return type ( < E > in the next example). Each type parameter section contains one or more type parameters separated by commas.
To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". This introduces the type variable, T, that can be used anywhere inside the class. As you can see, all occurrences of Object are replaced by T.
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();
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