我有以下示例:
class uncheckedreturn
{
static abstract class Abstract
{
abstract public <A extends Abstract> A make ();
}
static class Concrete extends Abstract
{
public Concrete make ()
{
return new Concrete();
}
}
public static void main (String[] args) {}
}
Abstract
类要求定义make
方法,而Concrete
类正在扩展Abstract
类并定义make
方法。当我编译它时,我收到以下警告:
$ javac -Xlint:unchecked uncheckedreturn.java && java uncheckedreturn
uncheckedreturn.java:10: warning: [unchecked] make() in Concrete overrides <A>make() in Abstract
public Concrete make ()
^
return type requires unchecked conversion from Concrete to A
where A is a type-variable:
A extends Abstract declared in method <A>make()
1 warning
我不明白这里发生了哪次转换以及未选中的转换原因。为扩展make
的类型指定了抽象方法Abstract
。而Concrete
正是这样做的:扩展Abstract
。那么为什么Concrete
与A
不同?
如何更改代码以消除警告?
更新
我认为我更进一步了解代码中的问题是什么。通用抽象方法make
允许以下实现显然不是我想要允许的:
class uncheckedreturn
{
static abstract class Abstract
{
abstract public <A extends Abstract> A make ();
}
static class Concrete extends Abstract
{
@Override
public OtherConcrete make ()
{
return new OtherConcrete();
}
}
static class OtherConcrete extends Abstract
{
@Override
public Concrete make ()
{
return new Concrete();
}
}
public static void main (String[] args) {}
}
也许没有办法让方法保持通用。
答案 0 :(得分:3)
正如其他人所指出的那样,这个要求几乎没有任何意义(除了看起来像是试图模仿clone()
方法)。
最初,您说make
方法的返回类型应该是Abstract
的“任何”子类型。这通常只是意味着使用协变返回类型,如下例所示:
class uncheckedreturn
{
static abstract class Abstract
{
abstract public Abstract make();
}
static class ConcreteA extends Abstract
{
@Override
public ConcreteA make()
{
return new ConcreteA();
}
}
static class ConcreteB extends Abstract
{
@Override
public ConcreteB make()
{
return new ConcreteB();
}
}
public static void main(String[] args)
{
ConcreteA a0 = new ConcreteA();
ConcreteA ma0 = a0.make();
ConcreteB b0 = new ConcreteB();
ConcreteB mb0 = b0.make();
}
// This is possible, but you'd like to not allow this
static class ConcreteC extends Abstract
{
@Override
public ConcreteA make()
{
return new ConcreteA();
}
}
}
正如评论中所提到的(并在您的编辑中强调),这不会阻止类返回不属于其“自己”类型的Abstract
子类型,并且您明确表示您基本上正在寻找“this
- 输入”可以让你写出像
class Concrete {
public <SameAsThis> make() {
return new Concrete();
}
}
但Java中不存在这样的东西。在某种程度上,可以使用Curiously recurring template pattern来实现:您可以为类确定方法的返回类型的类型参数,并确保此类型与类匹配:
class uncheckedreturn
{
static abstract class Abstract<A extends Abstract<?>>
{
abstract public A make();
}
static class ConcreteA extends Abstract<ConcreteA>
{
@Override
public ConcreteA make()
{
return new ConcreteA();
}
}
static class ConcreteB extends Abstract<ConcreteB>
{
@Override
public ConcreteB make()
{
return new ConcreteB();
}
}
// // Now, this is no longer possible:
// static class ConcreteB extends Abstract<ConcreteB>
// {
// @Override
// public ConcreteA make()
// {
// return new ConcreteA();
// }
// }
// But this still IS possible, but may also not be desired...
static class ConcreteC extends Abstract<ConcreteA>
{
@Override
public ConcreteA make()
{
return new ConcreteA();
}
}
public static void main(String[] args)
{
ConcreteA a0 = new ConcreteA();
ConcreteA ma0 = a0.make();
ConcreteB b0 = new ConcreteB();
ConcreteB mb0 = b0.make();
}
}
但是,有一种情况是 class 类型参数的类型可能是“错误的”,这将允许它使用“错误”返回类型实现该方法。
另外,你说你想“只保留方法通用”,但此时根本就没有类型信息。基本上有两件事可以确定返回值的编译时类型:
但他们都不能满足你所说的要求:
Abstract
)但不包含上限或具体类型从更高层次的角度来看,这种方法可能有其他选择。在此处使用Factory
可能更合适,一次为Factory<ConcreteA>
,一次为Factory<ConcreteB>
(或仅为Java 8 Supplier)。但这取决于总体目标和这些类的预期用途。
答案 1 :(得分:0)
看起来您希望make
方法能够返回子类中变化的类型的对象,因此您应该为该类型创建泛型类型参数,并且具体实现可以应用适当的它所做类型的类型参数:
static abstract class Abstract<A> {
abstract public A make();
}
static class Concrete extends Abstract<Concrete> {
public Concrete make() {
return new Concrete();
}
}