Java泛型 -> 函数返回类型

11

我有一个这样的情况:

我有一个类,看起来像这样:

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块是不可行的。

请提出一种优雅的方法。

编辑:示例用法:

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

在这种情况下,如果tc1tc2来自创建这些对象的工厂,我想知道创建AnotherClass实例的合适方式是什么。

3
你的测试方法应该做什么?你能展示一下你的工厂方法吗? - Rohit Jain
2
为了澄清,您能添加一下您想要替换的 if/else 的摘录吗? - user180100
你可以将方法参数化为所需的类型。我刚刚添加了一个带有示例代码的答案。 - Petro Semeniuk
2
你的例子缺乏上下文,所以很难推荐任何东西。例如,testMethod的返回值是什么?method不是一个有效的返回类型;你是想说void吗?如果它是一个void方法,那么可能testMethod中的K没有任何好处,你可以只接受一个TestClass<?>。但这都是猜测。更有可能的是,你需要纠正你的工厂,因为拥有一个在编译时未知的泛型类型的泛型工厂是无用的。 - Mark Peters
2
@LPD:我没有看到这样的情况或示例代码。你在testMethod中添加了一个注释,但那不是我要求的。我要求的是使用testMethodsampleFactory的示例代码。几乎总是,如果类型参数只在运行时知道,将testMethod泛型化并没有什么好处,因为K永远不会在编译时解析为任何内容。缺乏好的答案表明你的问题缺乏上下文(尽管它获得了奇怪数量的赞)。 - Mark Peters
显示剩余8条评论
4个回答

5

你的问题与这个方法有关:

public TestClass<?> sampleFactory(int i) {
?通配符类型表示“某种类型,但我不知道是什么”。因此,您可以获得类型为TestClass<?>的值,但这对您没有用,因为您无法有意义地与类型?交互--您不能创建类型为?的值(除了null),也不能调用类型?的方法(除了java.lang.Object的方法)。
您真正想要的是类似于:
public <T> TestClass<T> sampleFactory(TypeToken<T> typeToken) {

换句话说,如果您希望工厂返回由不同类型参数化的值,则需要提供一些告诉它您想要的类型的东西。不幸的是,“int”并不足够——您可能知道“i==1”意味着类型将是“Integer”,但编译器不知道。
您对问题的描述有点含糊,让我难以理解您真正想要实现什么,但我的猜测是,您真正需要的是类似于超级类型标记或者像Guava的ClassToInstanceMap这样的东西。

2

一个可能的解决方案是使用原始的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) {
    }
}

编译没问题。但是一般情况下不建议使用原始类型


这段代码是否无论何种原因都不会抛出任何异常?它使用起来安全吗? - LPD
@LPD,我的确切代码(包括空方法等)运行良好。我不确定它是否适用于你的具体情况。这取决于testMethod中如何使用K - RiaD
我编译了相同的代码。它可以运行。但是我不知道它有多安全。 - LPD
@LPD。我能想象到的唯一问题是当您将某些类型的对象用作另一种类型的对象时,例如tesMethod将返回Integer,因为您使用了原始类型,所以您将其作为Object获取,然后将其强制转换为String,因为您认为K是字符串,而不是Integer - RiaD

0

Java文档说:

原始类型C的构造函数、实例方法或非静态字段M的类型(该类型不是从其超类或超接口继承而来)是对应于C的泛型声明中其类型的擦除。原始类型C的静态成员的类型与对应于C的泛型声明中其类型相同。

即使方法的类型签名不使用类本身的任何类型参数,类型擦除也会出现。

这就是您的方法变成的样子,

public method testMethod(TestClass param) {
}

你的工厂方法返回一个类型为TestClass<?>的对象,无法适应上述方法。


我明白。那么如果-否则的代码块是唯一的解决方案吗? - LPD
这与擦除无关。问题出现在根本没有使用原始类型的情况下。实际上,使用原始类型时,它实际上会带有一些警告而工作(请参见其他答案)。只有在非原始情况下编译器错误才会发生。 - Mark Peters

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

}

在 OP 的示例中,返回值的类型只有在运行时才知道(取决于工厂方法的参数)。 - RiaD
不完全是,你在源代码中指定了类型,因此必须在编译时知道它。对吧? :-) - Petro Semeniuk
3
不行。考虑情况 sampleFactory(getIntFromUserInput()) - RiaD
是的@RiaD。我正要说同样的话。而getIntFromUserInput实际上是从Oracle表中读取的。所以无论如何,我们都必须退回到if else块。 - LPD
1
@RiaD:仅在运行时才能确定的通用类型由于擦除往往是完全无用的。 - Mark Peters
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接