Подтвердить что ты не робот

Java Generics → Тип возврата функции

У меня такая ситуация:

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

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

Теперь у меня есть метод factory, который возвращает объект типа 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>();
}

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

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

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

ИЗМЕНИТЬ: Использование примера:

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 поступают из factory, который создает эти объекты, я хочу знать, какой достойный способ создать экземпляр AnotherClass

4b9b3361

Ответ 1

Ваша проблема заключается в этом методе:

public TestClass<?> sampleFactory(int i) {

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

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

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

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

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

Ответ 2

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

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) {
    }
}

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

Ответ 3

Java Doc говорит:

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

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

Это ваш метод,

public method testMethod(TestClass param) {
}

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

Ответ 4

Используя этот, вы сможете передать параметр в 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));
    }

}