如何避免警告:返回类型需要未经检查的转换?

时间:2014-07-15 15:41:09

标签: java generics inheritance

我有以下示例:

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。那么为什么ConcreteA不同?

如何更改代码以消除警告?

更新

我认为我更进一步了解代码中的问题是什么。通用抽象方法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) {}
}

也许没有办法让方法保持通用。

2 个答案:

答案 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();
  }
}