Reputation: 844
I have a method like this-
public List<Apples> returnFruits(String arg1){
List<Apples> fruits = new ArrayList<Apples>();
Apple a = new Apple();
fruits.add(a);
return fruits;
}
I would like to change it, so that I can specify the fruit type from the method call and return that fruit list. So the 2nd statement should dynamically instantiate the list of fruits that I pass. I thought of this-
public List<?> returnFruits(String arg1){
List<T> fruits = new ArrayList<T>();
T t = new T();
fruits.add(t);
return fruits;
}
But don't know the right way to do it, as you can see.
In the second method, I just return the fruit instead of the list-
public T returnFruit(){
T t = new T();
return t;
}
The fruits that are passed are NOT in the same class hierarchy and are different types.
Thank you.
Upvotes: 3
Views: 690
Reputation: 2992
If you know for sure that you'll have a no argument constructor, you could use this syntax :
public <T> List<T> returnFruits(Class<T> clazz){
List<T> fruits = new ArrayList<T>();
T t = clazz.newInstance();
fruits.add(t);
return fruits;
}
Usage :
List<MyClass> m = returnFruits(MyClass.class, "plop");
If you know you have a constructor with a String parameter :
public <T> List<T> returnFruits(Class<T> clazz, String arg1){
List<T> fruits = new ArrayList<T>();
Constructor<T> constructor = clazz.getConstructor(String.class);
T t = constructor.newInstance(arg1);
fruits.add(t);
return fruits;
}
Usage :
List<MyClass> m = returnFruits(MyClass.class, "plop");
Etc.
Upvotes: 6
Reputation: 43391
There are a number of approaches you could take. One is to use Class<T>
as others have suggested. Another would be to create some sort of producer interface, and pass that in:
public interface FruitProducer<T> {
T createFruit(String arg);
}
public <T> List<T> returnFruits(String arg, FruitProducer<? extends T> producer) {
List<T> list = new ArrayList<T>();
T fruit = producer.createFruit(arg);
list.add(fruit);
return list;
}
You have different producers for different fruits: AppleProducer implements FruitProducer<Apple>
, OrangeProducer implements FruitProducer<Orange>
, etc. This approach just kicks the can a bit -- the FruitProducer
still has to create the fruits somehow -- but it could be a useful refactoring.
Yet another approach relies on the same polymorphism as the FruitProducer
approach by making the class with returnFruits
abstract:
public abstract class FruitLister<T> {
public abstract List<T> returnFruits(String arg);
}
Now you have different listers: AppleLister implements FruitLister<Apple>
, etc. Each knows how to instantiate the specific classes it needs to return in a list:
public class AppleLister implements FruitLister<Apple> {
@Override
public List<Apple> returnFruits(String arg) {
List<Apple> list = new ArrayList<Apple>();
Apple apple = new Apple(arg);
list.add(apple);
return list;
}
}
Upvotes: 4
Reputation: 1247
<T> public List<T> returnFruit(){
Apple a = new Apple();
List<Apple> list = new ArrayList<Apple>;
list.add(a);
return list;
}
should work. Basically, declare the fact that you want a list of type "T" just before the method. Then you can return a List of any type "T". Basically, you cannot do a "new T()" because it does not have any type information. It has to be a concreate class.
You can even pass in that class type as a param if you want.
Upvotes: 0
Reputation: 156469
Java implements generics via erasure, so there is no notion at runtime of which generic type you passed in. Neither is it possible to new
up a generic type, because you cannot guarantee that the type passed in will have a parameterless constructor.
You can pass in the class itself as a real parameter:
public <T> List<T> returnFruits(String arg1, Class<T> clazz){
... but then you need to use reflection to instantiate a new version of that class, and just cross your fingers to hope that users don't provide a class with no default constructor.
Upvotes: 2