codebee
codebee

Reputation: 844

Java- Using generics to return more than one type

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

Answers (4)

Nicocube
Nicocube

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

yshavit
yshavit

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

Pavan
Pavan

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

StriplingWarrior
StriplingWarrior

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

Related Questions