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”为classA
的方法。由此可以推断返回的类型也是类A
(asT
根据方法code返回,asT
实例化为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”为classA
的方法。由此可以推断返回的类型也是类A
(asT
根据方法code返回,asT
实例化为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