Java 扩展另一个泛型类的泛型类的泛型类型参数出现绑定不匹配错误

Java 扩展另一个泛型类的泛型类的泛型类型参数出现绑定不匹配错误,java,generics,inheritance,Java,Generics,Inheritance,我对java中的泛型和继承有问题 我想从“createManager”方法实例化一个“ObjectManager”类,该类可以从给定类型创建实例。此“ObjectManager”具有一些实用程序方法(例如:“getDescription”) 这个方法允许我从一个类型实例化一个“ObjectManager”,然后获取它的描述(这里的散列) 如果输入类型可分配给类型“Foo”,那么我还想将“ObjectManager”专门化为“FooManager”,这样我就可以重写“getDescription”

我对java中的泛型和继承有问题

我想从“createManager”方法实例化一个“ObjectManager”类,该类可以从给定类型创建实例。此“ObjectManager”具有一些实用程序方法(例如:“getDescription”)

这个方法允许我从一个类型实例化一个“ObjectManager”,然后获取它的描述(这里的散列)

如果输入类型可分配给类型“Foo”,那么我还想将“ObjectManager”专门化为“FooManager”,这样我就可以重写“getDescription”方法。因此,我在“createManager”方法中测试输入的类型,以了解是创建“ObjectManager”还是“FooManager”

代码可以工作,但我不知道与“newfoomanager(type)”对应的输出的泛型类型。在我看来,对于Eclipse Quick fix,类型应该是,但这会导致错误:

绑定不匹配:类型T不是类型FooManager的绑定参数的有效替代品

我的代码:

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