Java - Использование обобщений для возврата более одного типа - PullRequest
3 голосов
/ 24 февраля 2012

У меня есть такой метод -

public List<Apples> returnFruits(String arg1){
List<Apples> fruits = new ArrayList<Apples>();
Apple a = new Apple();
fruits.add(a);

return fruits;
}

Я хотел бы изменить его, чтобы я мог указать тип фруктов из вызова метода и вернуть этот список фруктов. Поэтому второе утверждение должно динамически создавать список фруктов, которые я передаю. Я думал об этом -

public List<?> returnFruits(String arg1){
List<T> fruits = new ArrayList<T>();
T t = new T();
fruits.add(t);

return fruits;
}

Но вы не знаете, как это сделать, как видите.

Во втором методе я просто возвращаю фрукты вместо списка -

public T returnFruit(){
T t = new T();
return t;
}

Полученные плоды НЕ находятся в одной иерархии классов и являются разными типами.

Спасибо.

Ответы [ 4 ]

6 голосов
/ 24 февраля 2012

Если вы точно знаете, что у вас будет конструктор без аргументов, вы можете использовать этот синтаксис:

public <T> List<T> returnFruits(Class<T> clazz){
    List<T> fruits = new ArrayList<T>();
    T t = clazz.newInstance();
    fruits.add(t);

    return fruits;
}

Использование:

List<MyClass> m = returnFruits(MyClass.class, "plop");

Если вы знаете, что у вас естьконструктор с параметром String:

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;
}

Использование:

List<MyClass> m = returnFruits(MyClass.class, "plop");

И т. д.

4 голосов
/ 24 февраля 2012

Есть несколько подходов, которые вы можете использовать.Одним из них является использование Class<T>, как предлагали другие.Другим было бы создать некоторый интерфейс производителя и передать его в:

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;
}

У вас есть разные производители для разных фруктов: AppleProducer implements FruitProducer<Apple>, OrangeProducer implements FruitProducer<Orange> и т. Д. Этот подход просто пинает банкунемного - FruitProducer все еще должен как-то создавать плоды - но это может быть полезным рефакторингом.

Еще один подход основан на том же полиморфизме, что и подход FruitProducer, делая класс с returnFruits аннотация:

public abstract class FruitLister<T> {
    public abstract List<T> returnFruits(String arg);
}

Теперь у вас есть разные списки: AppleLister implements FruitLister<Apple> и т. Д. Каждый знает, как создавать конкретные классы, которые ему нужно вернуть в список:

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;
    }
}
2 голосов
/ 24 февраля 2012

Java реализует обобщенные значения с помощью стирания, поэтому во время выполнения не существует понятия, какой обобщенный тип вы передали. Также невозможно new создать обобщенный тип, поскольку вы не можете гарантировать, что передаваемый тип будет иметь параметры без параметровКонструктор.

Вы можете передать сам класс в качестве реального параметра:

public <T> List<T> returnFruits(String arg1, Class<T> clazz){

... но тогда вам нужно использовать отражение, чтобы создать новую версию этого класса, и простоскрестите пальцы, чтобы надеяться, что пользователи не предоставляют класс без конструктора по умолчанию.

0 голосов
/ 24 февраля 2012
<T> public List<T> returnFruit(){
Apple a = new Apple();
List<Apple> list = new ArrayList<Apple>;
list.add(a);
return list;
}

должно работать.По сути, объявите тот факт, что вы хотите список типа "T" непосредственно перед методом.Затем вы можете вернуть список любого типа "T".По сути, вы не можете сделать «новый T ()», потому что он не имеет никакой информации о типе.Это должен быть конкретный класс.

Вы можете даже передать этот тип класса в качестве параметра, если хотите.

...