Java泛型和不兼容类型使用<S extends {class}>

6
abstract class AbsClass {}

class MyAbs extends AbsClass {}

class MyClass<S extends AbsClass> {
    S getObj() {
        return new MyAbs();
    }
}

遇到编译问题:

错误:(33, 16) java: 不兼容的类型:MyAbs无法转换为S

应该如何正确解决?

编辑: 我希望能够初始化MyClass{MyAbs},然后调用getObj()方法,该方法将返回一个MyAbs对象。根据Andy的答案,我需要将AbsClass强制转换为MyAbs或MySecondAbs,这正是我想避免的。


2
移除 <S extends AbsClass>,并将 S getObj() 更改为 MyAbs getObj()AbsClass getObj()。目前来看,没有必要使用类型变量。 - Andy Turner
1
你能描述一下说“做这个”时你想要什么吗? - Simon
我希望能够初始化MyClass<MyAbs>,然后调用getObj()方法,该方法将返回一个MyAbs对象。根据Andy的答案,我必须将AbsClass强制转换为MyAbs或MySecondAbs,这正是我想避免的。 - mwong56
3个回答

5

Andy已经描述了如何解决问题,我会尝试解释为什么。

S不能保证可分配给MyAbs,只能分配给AbsClass,每个实例将指定AbsClass的子类。

考虑以下情况:

class MyOtherAbs extends AbsClass {}

MyClass myClass = new MyClass<MyOtherAbsClass>{};

这将与您拥有的内容产生冲突。
更新:
根据您的评论,看起来您想实现上述内容。挑战在于,如果MyOtherAbs具有带参数的构造函数或作为Singleton实现(即具有私有构造函数和静态getInstance()),该怎么办?简而言之,没有保证统一的构造方式。此外,由于类型擦除,在运行时,S的概念已经消失,因此您无法执行类似以下的操作:
return new S();

你基本上有两种选择,一种是使用子类化:

public interface AbsClassFactory<S extends AbsClass>{ //takes the place of MyClass
    public S getInstance();
}

MyAbsFactory implements AbsClassFactory<MyAbs>{
    public MyAbs getInstance(){
        return new MyAbs(); //or however MyAbs is instantiated
    }
}

另外一种选择是反射(reflection):
class MyClass {
    public <S extends AbsClass> S getObj(Class<S> clazz) throws InstantiationException, IllegalAccessException {
        return clazz.newInstance();
    }
}

请注意,第二种情况假定存在无参构造函数,如果没有则会抛出运行时异常。

您的示例正是我想要实现的,抱歉表述不清。 - mwong56
我会扩展答案,提供一种替代方案。 - Taylor

3

S继承自MyAbs,而不是反过来。任何类型为S的对象都可以转换为MyAbs,但MyAbs不能被转换为其派生类。

请解释你想要实现什么。


1
所有贵宾犬都是狗,但并非所有狗都是贵宾犬。 - Taylor

1

根据您的评论,似乎可以使用Supplier接口完成相同的操作:

Supplier<MyAbs> supp = MyAbs::new; // Constructor reference
MyAbs obj = supp.get();

Supplier<MySecondAbs> supp2 = MySecondAbs::new;
MySecondAbs obj2 = supp2.get();

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