Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 731
Sathish Avatar asked Jan 16 '09 15:01

Sathish


People also ask

How do you create a generic method?

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.

How do you make a generic type in Java?

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.


2 Answers

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.

like image 118
laz Avatar answered Sep 22 '22 06:09

laz


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(); 
like image 41
David Schmitt Avatar answered Sep 23 '22 06:09

David Schmitt