Java 泛型方法返回类型-编译错误

Java 泛型方法返回类型-编译错误,java,generics,Java,Generics,给定此代码示例: class A { } public class TestGenerics { private static <T extends A> T failsToCompile() { return new A(); } private static <T extends A> T compiles(T param) { return param; } } A类{ } 公共类测试泛型{

给定此代码示例:

class A {

}

public class TestGenerics {

    private static <T extends A> T failsToCompile() {
        return new A();
    }

    private static <T extends A> T compiles(T param) {
        return param;
    }

}
A类{
}
公共类测试泛型{
私有静态T failsToCompile(){
返回新的A();
}
私有静态T编译(T参数){
返回参数;
}
}
为什么第一个方法不编译,而第二个方法编译

错误是:

不兼容的类型。必填项:T。查找项:A

本质上,我试图实现的是返回子类型的一个具体实例(例如,扩展了a的类B)。有点像工厂的方法

编辑:好的,对于那些投票被否决的人,我决定进一步阐述。请参阅更新的示例,该示例不再使用字符串(是的,我知道字符串是最终的,不需要说明显而易见的内容)

编辑2:问题的备选版本:

您将如何实现此方法以使其能够编译(没有未检查的强制转换和警告)

abstract T failsToCompile();
编辑3:下面是一个代码示例,更接近我试图解决的问题:

class Foo { }
class Bar extends Foo { }
class A<T extends Foo> { }
class B extends A<Bar> { }
public class TestGenerics {

    private static <T extends Foo> A<T> failsToCompile() {
        return new B();
    }

}
class Foo{}
类栏扩展了Foo{}
A类{}
类B扩展了{}
公共类测试泛型{
私有静态A failsToCompile(){
返回新的B();
}
}

这里,方法返回类型是
A
。考虑到
T
被定义为Foo或其子类,而B的定义如下:
B扩展了a
,为什么我不能返回
new B()
?我想问题归结为您不能分配
List dList=new ArrayList()我明白为什么。但是有一个优雅的解决方案吗?

忽略
String
final
这一事实(因此您不能有
String
的子类),您可以将泛型类
class
传递到泛型案例的方法中。像

private static <T extends String> T get(Class<T> cls) 
            throws InstantiationException, IllegalAccessException {
    return cls.newInstance();
}
private static T get(类cls)
抛出实例化异常,IllegalAccessException{
返回cls.newInstance();
}

忽略
String
final
这一事实(因此您不能有
String
的子类),您可以将泛型类
class
传递到泛型案例的方法中。像

private static <T extends String> T get(Class<T> cls) 
            throws InstantiationException, IllegalAccessException {
    return cls.newInstance();
}
private static T get(类cls)
抛出实例化异常,IllegalAccessException{
返回cls.newInstance();
}

我试图将我的评论更详细地解释为对“为什么”的回答

您在问题中显示的是泛型方法(而不是泛型类型),请参见例如

推理

让我们考虑对工作方法的显式调用:<代码> TestPrimic。编译(new A.)(<代码>

这将实例化对类
A
的方法调用的泛型类型
T
。请记住,类型擦除在编译时会删除所有这些时髦的泛型类型,JVM看不到泛型类型,编译器会处理泛型。编译器现在知道,您想要调用泛型类型为
T
“set”为class
A
的方法。由此可以推断返回的类型也是类
A
(as
T
根据方法code返回,as
T
实例化为
A
)。这就是使用泛型的全部好处

您可以从方法调用中删除
,使其看起来像“普通”方法调用,因为编译器可以推断
T
必须是
A
TestGenerics.compiles(新的A())

让我们看看
TestGenerics.compiles(新的B())类B的code>扩展了{}
。如上所述,编译器知道此方法调用将返回类
B
的对象

现在想象一下(不编译)代码
TestGenerics.compiles(新的A())。由于传递给方法的对象不是
B
类型,编译器将抛出错误。否则,该方法将返回类
A
的对象,但泛型类型断言该方法在这里返回类型
B
的对象。 这实际上相当于安德烈亚斯在评论中给出的示例(
bb=failsToCompile()

在实例化泛型类型之前——即使设置了边界(比如
T扩展了一个
),它也没有类类型。因此,不能返回具体的类,因为这不满足泛型类型参数

比萨饼的例子 为了好玩,让我们试着为上面的推理做一个真实的例子:比萨饼。在本例中,我们假设每个比萨都是玛格丽塔的一个子类型,即,你在玛格丽塔中添加配料,得到你最喜欢的其他比萨

class PizzaMargherita {};

class PizzaProsciutto {
  PizzaProsciutto() {
    super(); 
    addIngredient(new Prosciutto());
  }
}
您的
compiles()
方法现在烘焙比萨饼:

public static <P extends Margherita> P bake(P pizza) {
  heat(pizza); 
  return pizza;
}
您仍然需要某种形式的映射来获得正确的工厂来构建所需的类,但是现在您可以在需要创建T类型对象的地方使用任何可用的映射

Map<String, Factory<?>> factories = new HashMap<>();
Map<DAO, Factory<?>> factories = new HashMap<>();
Map<ValueObject, Factory<?>> factories = new HashMap<>();
factories.get(valueObjectTypeA);
Map>factories=newhashmap();

映射我试图将我的评论更详细地解释为对“为什么”的回答

您在问题中显示的是泛型方法(而不是泛型类型),请参见例如

推理

让我们考虑对工作方法的显式调用:<代码> TestPrimic。编译(new A.)(<代码>

这将实例化对类
A
的方法调用的泛型类型
T
。请记住,类型擦除在编译时会删除所有这些时髦的泛型类型,JVM看不到泛型类型,编译器会处理泛型。编译器现在知道,您想要调用泛型类型为
T
“set”为class
A
的方法。由此可以推断返回的类型也是类
A
(as
T
根据方法code返回,as
T
实例化为
A
)。这就是使用gen的所有好处
public static <P extends Margherita> P bakePizza(Ingredients<P> ingredient) {
  P pizza = buildPizza(ingredients);
  return bake(pizza);
}
private static <T extends A> T failedToCompile(Class<T> c)
        throws InstantiationException, IllegalAccessException {
    return c.newInstance();
}
class Factory<T extends A> {
  private final Class<T> typeToken;
  public Factory(Class<T> typeToken) {
    this.typeToken = typeToken;
  }

  public T build()
        throws InstantiationException, IllegalAccessException {
    return this.typeToken.newInstance();
  }
}
Map<String, Factory<?>> factories = new HashMap<>();
Map<DAO, Factory<?>> factories = new HashMap<>();
Map<ValueObject, Factory<?>> factories = new HashMap<>();
factories.get(valueObjectTypeA);
private static <T extends A> T nowItCompilesFine(Supplier<? extends T> factory) {
    return factory.get();
}
A a = nowItCompilesFine(() -> new A());
A a = nowItCompilesFine(A::new);
class Child extends A { }

List<Supplier<? extends A>> factories = Arrays.asList(A::new, Child::new);

Supplier<? extends A> factoryA = factories.get(0);

A a1 = nowItCompilesFine(factoryA);

Supplier<? extends A> factoryChild = factories.get(1);

A a2 = nowItCompilesFine(factoryChild);

System.out.println(a2.getClass().getSimpleName()); // Child