Java 扩展另一个泛型类的泛型类的泛型类型参数出现绑定不匹配错误
我对java中的泛型和继承有问题 我想从“createManager”方法实例化一个“ObjectManager”类,该类可以从给定类型创建实例。此“ObjectManager”具有一些实用程序方法(例如:“getDescription”) 这个方法允许我从一个类型实例化一个“ObjectManager”,然后获取它的描述(这里的散列) 如果输入类型可分配给类型“Foo”,那么我还想将“ObjectManager”专门化为“FooManager”,这样我就可以重写“getDescription”方法。因此,我在“createManager”方法中测试输入的类型,以了解是创建“ObjectManager”还是“FooManager” 代码可以工作,但我不知道与“newfoomanager(type)”对应的输出的泛型类型。在我看来,对于Eclipse Quick fix,类型应该是,但这会导致错误: 绑定不匹配:类型T不是类型FooManager的绑定参数的有效替代品 我的代码:Java 扩展另一个泛型类的泛型类的泛型类型参数出现绑定不匹配错误,java,generics,inheritance,Java,Generics,Inheritance,我对java中的泛型和继承有问题 我想从“createManager”方法实例化一个“ObjectManager”类,该类可以从给定类型创建实例。此“ObjectManager”具有一些实用程序方法(例如:“getDescription”) 这个方法允许我从一个类型实例化一个“ObjectManager”,然后获取它的描述(这里的散列) 如果输入类型可分配给类型“Foo”,那么我还想将“ObjectManager”专门化为“FooManager”,这样我就可以重写“getDescription”
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
public class GenericBug {
private GenericBug() {}
public static void main(String[] args) {
ObjectManager<Foo> objectManager1 = createManager(Foo.class);
System.out.println(objectManager1.getDescription());
ObjectManager<Object> objectManager2 = createManager(Object.class);
System.out.println(objectManager2.getDescription());
}
private static <T> ObjectManager<T> createManager(Class<T> type) {
if(Foo.class.isAssignableFrom(type))
return new FooManager<T>(type); //Error: Bound mismatch
return new ObjectManager<T>(type);
}
}
class ObjectManager<T>{
public T val;
public ObjectManager(Class<T> type) {
try {
val = type.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
public String getDescription() {
return "Hash: " + Objects.hash(val);
}
}
class FooManager<T extends Foo> extends ObjectManager<T>{
public FooManager(Class<T> type) {
super(type);
}
@Override
public String getDescription() {
return super.getDescription() + " value: " + val.getVal();
}
}
class Foo{
private double val = Math.random();
public Foo() {}
public double getVal() {
return val;
}
}
import java.lang.reflect.InvocationTargetException;
导入java.util.Objects;
公共类泛型错误{
私有GenericBug(){}
公共静态void main(字符串[]args){
ObjectManager objectManager1=createManager(Foo.class);
System.out.println(objectManager1.getDescription());
ObjectManager objectManager2=createManager(Object.class);
System.out.println(objectManager2.getDescription());
}
私有静态ObjectManager createManager(类类型){
if(Foo.class.isAssignableFrom(type))
返回新的FooManager(类型);//错误:绑定不匹配
返回新的ObjectManager(类型);
}
}
类对象管理器{
公共T值;
公共对象管理器(类类型){
试一试{
val=type.getConstructor().newInstance();
}catch(实例化异常| IllegalAccessException | IllegalArgumentException |调用目标异常| NoSuchMethodException |安全异常e){
e、 printStackTrace();
}
}
公共字符串getDescription(){
返回“Hash:+Objects.Hash(val);
}
}
类FooManager扩展了ObjectManager{
公共FooManager(类类型){
超级(型);
}
@凌驾
公共字符串getDescription(){
返回super.getDescription()+“值:”+val.getVal();
}
}
福班{
private double val=Math.random();
公共Foo(){}
公共双getVal(){
返回val;
}
}
所以我的问题是:为什么会出现这种错误?如何在没有警告的情况下修复它?另外,作为奖励,为什么Eclipse提供了一个不编译的解决方案?这是一个Eclipse错误吗
提前感谢您的回答。问题在于,您通过
isAssignableFrom
混合了泛型的编译时安全性和运行时检查
我不知道还有更好的方法:
private static <T> ObjectManager<T> createFooManager(Class<T> type) {
if (Foo.class.isAssignableFrom(type)) {
return (ObjectManager<T>) getIt((Class<? extends Foo>) type);
}
return new ObjectManager<>(type);
}
private static <R extends Foo> ObjectManager<R> getIt(Class<R> cls) {
return new FooManager<>(cls);
}
私有静态对象管理器createFooManager(类类型){
if(Foo.class.isAssignableFrom(type)){
return(ObjectManager)getIt((Class发生此错误的原因是您没有编译时保证T是Foo的实例。您只有运行时保证Foo可从T分配,而编译器并不关心这一点。您可以通过抑制它们来进行此编译,而不产生警告:
@SuppressWarnings({ "unchecked", "rawtypes" })
private static <T> ObjectManager<T> createFooManager(Class<T> type) {
if(Foo.class.isAssignableFrom(type))
return new FooManager(type); //Error: Bound mismatch
return new ObjectManager<T>(type);
}
@SuppressWarnings({“unchecked”,“rawtypes”})
私有静态ObjectManager createFooManager(类类型){
if(Foo.class.isAssignableFrom(type))
返回新的FooManager(类型);//错误:绑定不匹配
返回新的ObjectManager(类型);
}
这可能不是您想要的答案,但我认为您无法在不抑制警告的情况下消除这些警告。我从这些警告的不可避免性中推断出,最初的方法是不正确的。为什么您需要将类作为参数传入?如果在编译时知道类参数,您可以可以简单地为不同的类调用不同的方法。如果在编译时不知道该类,那么您永远都不应该从该泛型参数中获益
“为什么会出现此错误?”
javac
在原始代码上报告的错误给出了以下原因:
...GenericBug.java:14: error: type argument T#1 is not within bounds of type-variable T#2
return new FooManager<T>(type); //Error: Bound mismatch
^
where T#1,T#2 are type-variables:
T#1 extends Object declared in method <T#1>createFooManager(Class<T#1>)
T#2 extends Foo declared in class FooManager
1 error
您的IRL用例是什么并不明显。因此,我不得不做出一些假设。就像我假设您很可能会在更专业的Foo
实现中使用它一样。因此,我引入了FooJr
来演示解决方案将如何处理这一问题:
ObjectManager<? extends Foo> objectManager1 = createFooManager(Foo.class);
System.out.println(objectManager1.getDescription());
ObjectManager<?> objectManager2 = createObjectManager(Object.class);
System.out.println(objectManager2.getDescription());
objectManager1 = createFooManager(FooJr.class);
System.out.println(objectManager1.getDescription());
objectManager2 = createObjectManager(FooJr.class);
System.out.println(objectManager2.getDescription());
ObjectManager objectManager2=createObjectManager(Object.class);
System.out.println(objectManager2.getDescription());
objectManager1=createFooManager(FooJr.class);
System.out.println(objectManager1.getDescription());
objectManager2=createObjectManager(FooJr.class);
System.out.println(objectManager2.getDescription());
说一种解决方案比另一种解决方案“更好”或“更差”是主观的;可能是个人品味的问题。但可以客观地说(双关语)一种解决方案比另一种解决方案更面向对象。某些解决方案肯定比其他解决方案更安全
“在没有警告的情况下……”
可证明更安全。使用-Xlint:unchecked
运行它。编译器不会报告任何未检查的警告。这不需要任何@SuppressWarnings(…)
“为什么Eclipse提供不编译的解决方案?”
Eclipse只是向您提供了它认为解决问题的最佳猜测。IDE的本质是让某些东西通过javac
或JLS绝对不允许的操作。出现此错误是因为FooManager
需要一个扩展的类型<
ObjectManager<? extends Foo> objectManager1 = createFooManager(Foo.class);
System.out.println(objectManager1.getDescription());
ObjectManager<?> objectManager2 = createObjectManager(Object.class);
System.out.println(objectManager2.getDescription());
objectManager1 = createFooManager(FooJr.class);
System.out.println(objectManager1.getDescription());
objectManager2 = createObjectManager(FooJr.class);
System.out.println(objectManager2.getDescription());