Java 抽象方法中使用通配符和声明泛型的区别

Difference between using wildcards and declaring generic type in abstract method in Java

我试图理解 Java 中的泛型类型,理论上它看起来可以理解,但是当我需要将它应用到实际代码时我遇到了问题。我想声明将 return 泛型类型的抽象方法。假设我有一些名为 Magicable 的空接口,并且 2 class 实现了它:Magican 和 。 现在我想知道这 3 个声明之间有什么区别:

/*1*/protected abstract <T extends Magicable> List<T> getMagicables();
/*2*/protected abstract List<? extends Magicable> getMagicables();
/*3*/protected abstract List<Magicable> getMagicables();
  1. 在第一种情况下,当我想在扩展抽象 class:

    的某些 class 中实现此方法的主体时遇到问题
    @Override
    protected List<Magican> getMagicable() {..}
    

    我有警告消息:

    Type safety: The return type List<Magican> for getMagicable() from the type MagicanService needs unchecked conversion to conform to List<Magicable> from the type MagicableService.

  2. 在第二种情况下我没有这个警告,但是我在抽象 class 中有问题,我在上面声明了抽象方法:

      public void <T extends Magicable> T getOneFromList() {
          List<T> list = getMagicables();
          //.....
      }
    

    在这种情况下,我在 getMagicables() 调用中出现编译错误:

    Type mismatch: cannot convert from List<capture#2-of ? extends Magicable> to List<T>

  3. 第三种情况导致上述两处代码编译错误。我不认为它是否适合我的情况。

对于问题的部分,您确实向我们展示了方法 /* 3 */ 就足够了,您的那部分代码不需要泛型。但你需要尊重可替代性:

你在 #1 中得到错误,因为子类型方法限制了 return 类型的范围:MagicanMagicable 但反之则不然。子类型中允许 Magicable 的超类型。子类型方法必须可以替代超类型方法,而在您的示例中不是这种情况。

#2 中的错误是由于通配符 ? 的性质造成的:? extends MagicableT extends Magicable 不必是同一类型。如果 T 在 class 范围内声明,例如class Magican<T> implements Magicable<T>(当然,在这种情况下,您的接口需要声明 T)在您的类型中出现的所有 T 都将引用相同的 class.

public abstract class AbstractMagicable<T extends Magicable> {

    abstract List<T> getMagicables1();

    abstract List<? extends Magicable> getMagicables2();

    abstract List<Magicable> getMagicables3();
}

class MagicableWitch extends AbstractMagicable<Witch> {

    @Override
    List<Witch> getMagicables1() {
        return null;
    }

    @Override
    List<? extends Magicable> getMagicables2() {
        return getMagicables1();
    }

    @Override
    List<Magicable> getMagicables3() {
        return Collections.singletonList(new Witch());
    }   
}

class MagicableMagician extends AbstractMagicable<Magician> {

    @Override
    List<Magician> getMagicables1() {
        return null;
    }

    @Override
    List<? extends Magicable> getMagicables2() {
        return getMagicables1();
    }

    @Override
    List<Magicable> getMagicables3() {
        return Collections.singletonList(new Magician());
    }
}

1) 当你想在使用它时用真实姓名替换它时使用T。例如 class MagicableWitch extends AbstractMagicable<Witch>

此处 Witch 已替换 T,因此 abstract List<T> getMagicables1(); 在其具体 class.

中更改为 List<Witch> getMagicables1()

2)? class 将在运行时可用时使用 class 将被替换。

3) List<Magicable>List<Witch> 是不同的,尽管 Witch implements MagicablegetMagicables3 中显示了实现 .

在您的第一种情况下,抽象方法被声明为使用通用类型 <T extends Magicable>,这意味着您的方法可以 return Magicable 列表或实现它的任何类型。在您的实现中,您正在 return 使用具体类型 Magican,它是一个 Magicable。您可以安全地忽略警告并添加 @SuppressWarning("unchecked") 以禁用警告。需要注意的是,任何扩展您的 class 的 classes 都将被限制为 return 仅魔法师列表。

在第二种情况下,声明 List<T> list = getMagicables(); 会抛出错误,因为您的方法不是 return 一个 List<T> 而是一个 List<? extends Magicable',这不是一回事.由于泛型的工作方式,当您声明使用未绑定通配符的 return 类型时,任何调用您的方法的代码都必须具有可接受的匹配类型,在您的情况下如 List<? extends Magicable>List<?>.

关于第三种情况,您的抽象方法 return 是 List<Magicable> 而您的实现 return 是 List<Magic>。这似乎违反直觉,但您不能在 Java: List<Magicable> list = ArrayList<Magic> 中对泛型执行类似的操作。这可能看起来很奇怪,因为数组允许您声明类似 Magicable[] magics = new Magican[3]; 的东西。这是一个常见的误解,因为数组是协变的,而泛型是不变的。协变的意思是,如果你有两个 classes SuperSub extends SuperSub[] is a subtype of Super[]。对于泛型,因为它们是不变的,所以两者之间没有关系,List<Sub> 不是 List<Super> 的子类型。

如果你想 return 泛型类型,只需在 class 扩展你的抽象 class 中使用与第一个案例 protected <T extends Magicable> List<T> getMagicable() 相同的类型声明.在 returned 类型中使用通配符是一个非常糟糕的主意,因为您强制 class 用户在他们的 List 变量声明中使用通配符。

  1. First case

只需声明您的方法:

    @Override
    protected <T extends Magicable> List<T> getMagicables() {
       List<T> list = ...
       return list
    }

如果你真的想要这个:

    @Override
    protected List<Magican> getMagicable() {..}

您可能必须将通用 T 声明到 class 定义中

     public abstract class AbstractKlass<T extends Magicable> {
        protected abstract List<T> getMagicables();
     }

然后在你的 Subclass:

     public class MySubClass extends AbstractKlass<Magican> {

        @Override
        protected List<Magican> getMagicables() {
           ...
        }
     }
  1. Second case

编译错误是正常的,因为 <? extends Magicable> 方法的签名意味着从您可以将这些元素视为 Magicable 的那一刻起,您不关心列表中的内容。打电话时

    List<T> list = getMagicables();

你想在不知情的情况下照顾类型T。换句话说,有 3 个用例:T is Magicable (OK), T is Magician (Wrong because getMagicables may return a list of ) and T is (Wrong too ).

  1. Why I use ? extends Magicable instead of just Magicable in lists

因为 List<Magician>List<? extends Magicable> 的子类型,而不是 List<Magicable> 的子类型。这对方法的参数很有用。

    public void doIt(List<? extends Magicable> list) {
         // you can't add a Magician here
    }

可用作

    List<Witch> list = ...
    doIt(list);

但是如果你有

    public void doIt(List<Magicable> list) {
         // you can add a Magician here
    }

您不能将其用作

    List<Witch> list = ...
    doIt(list); // compile error