2013-08-10 2 views
11

У меня есть ситуации, как это:Java Дженерик -> Функция возвращает тип

У меня есть класс, который выглядит следующим образом:

public class TestClass<T> { 
    // class body here... 
} 

И у меня есть метод, который выглядит следующим образом:

public class AnotherTestClass<K> { 
    private TestClass<K> testClass; 

    public AnotherTestClass(TestClass<K> testClass) { 
     this.testClass = testClass; 
    } 

    public K testMethod() { 
     //call methods on param object and pass a value of the same type as testClass. 
     K returnVal = this.testClass.doSomething(); 
     return returnVal; 
    } 
} 

Теперь у меня есть фабричный метод, который возвращает объект типа TestClass<?>

public TestClass<?> sampleFactory(int i) { 
     if(i==1) 
      return new TestClass<Integer>(); 
     if(i==2) 
      return new TestClass<Double>(); 
     if(i==3) 
      return new TestClass<String>(); 
} 

Но я не могу использовать этот метод для передачи параметра в свой testMethod. Какое решение для этого?

В настоящее время я пишу if else цепных блоков, чтобы получить правильный экземпляр. Я знаю, что это неверно, поскольку нецелесообразно писать блоки if else, когда есть несколько параметров, таких как выше.

Пожалуйста, предложите элегантный способ для этого.

EDIT: Пример использования:

package my; 

import java.util.ArrayList; 
import java.util.List; 

public class GenericsSpike { 
    public static void main(String[] args) { 
     TestClass1<?> tc1 = new TestClass1<Integer>(123); 
     TestClass2<?> tc2 = new TestClass2<Integer>(123); 
     AnotherTestClass<?> atc = new AnotherTestClass<Integer>(tc1, tc2); 
     atc.testMethod(); 
    } 
} 

class TestClass1<T> { 
    private T value; 

    TestClass1(T val) { 
     value = val; 
    } 

    // class body here... 

    public T getValue() { 
     return value; 
    } 
} 

class TestClass2<T> { 
    private T value; 

    TestClass2(T val) { 
     value = val; 
    } 

    // class body here... 

    public T getValue() { 
     return value; 
    } 
} 

class AnotherTestClass<K> { 
    public TestClass1<K> testClass1, testClass2; 

    public AnotherTestClass(TestClass1<K> testClass, TestClass2<K> testClass2) { 
     this.testClass1 = testClass; 
    } 

    public K testMethod() { 
     //Any logic can come here. 
     System.out.println(testClass1.getValue()); 
     System.out.println(testClass2.getValue()); 
     return testClass1.getValue(); 
    } 
} 

В этом случае, если tc1 и tc2 приходят с завода, который создает эти объекты, я хочу знать, Что порядочный способ создания экземпляра AnotherClass

+3

Какой у вас должен быть метод testMethod? и можете ли вы показать свой заводской метод? –

+2

Чтобы уточнить, можете ли вы добавить выдержку 'if/else', которую вы хотите заменить –

+0

Спасибо за ваш ответ. Я отредактировал этот вопрос. Пожалуйста, дайте мне знать, если мне нужно добавить еще кое-что. – LPD

ответ

2

Одно из возможных решений состоит в использовании сырьевых AnotherTestClass Type

public class A { 
    public static void main(String[] args) { 
     TestClass<?> tc = new TestClass<Integer>(); 
     AnotherTestClass atc = new AnotherTestClass(); 
     atc.testMethod(tc); 
    } 
} 

class TestClass<T> { 

// class body here... 

} 

class AnotherTestClass<K> { 

    public void testMethod(TestClass<K> param) { 
    } 
} 

компилируется нормально. Но это не хорошая идея использовать сырые типы в общем случае

+0

Будет ли это исключать какие-либо исключения по какой-либо причине? Безопасно ли использовать? – LPD

+0

@LPD, мой точный код (с пустыми методами и т. Д.) Работает нормально. Я не уверен, что это сработает в вашем конкретном случае или нет. Это зависит от того, как 'K' используется в' testMethod' – RiaD

+0

Я скомпилировал то же самое. Он работает. Но я не знаю, насколько безопасно это – LPD

0

Java Doc говорит:

Тип конструктора, метода экземпляра или нестатического поле М исходного типа С, который не , унаследованный от его суперклассов или суперинтерфейсов, является стиранием его типа в общем объявлении , соответствующем C. Тип статического члена необработанного типа C совпадает с его типом в общем объявлении, соответствующем C

Тип стирание входит в картину, даже если тип подпись методы не использует какие-либо параметры типа самого класса,

Это то, что ваш метод становится

public method testMethod(TestClass param) { 
} 

Вашего фабричного метода, который возвращает объект типа TestClass<?>, не может быть размещен в вышеуказанном методе.

+0

. Я понимаю. Так что, если остальные блоки являются единственным решением? – LPD

+0

Это не имеет никакого отношения к стиранию. Проблема возникает, если вообще не использовать необработанные типы. На самом деле, с сырыми типами, он фактически будет работать с некоторыми предупреждениями (см. Другие ответы). ** В необработанном случае ** только ошибка компилятора. –

+0

_ Проблема возникает, когда вообще не используются необработанные типы ._ - Согласовано .. –

0

Используя это тот, который вы будете иметь возможность передать параметр к вашему testMethod:

package stackoverflow; 

public class Func { 

    static class TestClass<T> { 
    } 

    static class AnotherTestClass { 

     public <K> TestClass<K> testMethod(TestClass<K> param) { 
      return param; 
     } 
    } 

    static class Factory { 

     public static <E> TestClass<E> sampleFactory(int i) { 
      if (i == 1) 
       return (TestClass<E>) new TestClass<Integer>(); 
      if (i == 2) 
       return (TestClass<E>) new TestClass<Double>(); 
      if (i == 3) 
       return (TestClass<E>) new TestClass<String>(); 
      throw new IllegalArgumentException(); 
     } 
    } 

    public static void main(String[] args) { 
     new AnotherTestClass().testMethod(Factory.sampleFactory(1)); 
    } 

} 
+0

Тип возвращаемого значения известен только во время выполнения в примере OP (зависит от аргумента к методу фабрики) – RiaD

+0

Не совсем, вы указываете тип исходного кода поэтому он должен быть известен во время компиляции. Правильно? :-) –

+3

Эмм, нет. Рассмотрим случай 'sampleFactory (getIntFromUserInput())' – RiaD

5

Вашей проблемы с этим методом:

public TestClass<?> sampleFactory(int i) { 

? типа подстановочные означает «некоторый тип, но Я не знаю, что ». Таким образом, вы можете получить значение типа TestClass<?>, но это вам не полезно, потому что вы не можете эффективно взаимодействовать с типом ? - вы не можете создавать значения типа ? (кроме нулевого), и вы не можете позвонить методы по типу ? (кроме методов java.lang.Object).

То, что вы действительно хотите что-то вроде:

public <T> TestClass<T> sampleFactory(TypeToken<T> typeToken) { 

То есть, если вы хотите, чтобы ваш завод, чтобы дать вам обратно значения, параметризованные различными типами, вы должны дать ему то, что говорит ему, какой тип вы хотите , К сожалению, int не хватает - вы можете знать, что i==1 означает, что тип будет Integer, но компилятор этого не знает.

Ваше описание вашей проблемы слишком расплывчато для меня, чтобы понять, чего вы действительно пытаетесь достичь, но я предполагаю, что вам действительно нужно что-то вроде super type tokens или, может быть, что-то вроде Guava's ClassToInstanceMap.

Смежные вопросы