无界通配符类型 List<> 和原始类型 List 之间有什么区别?

2022-09-02 00:12:24

你能帮我理解无界通配符类型列表原始类型列表之间的区别吗

List<?> b;    // unbounded wildcard type
List a;       // raw type


除此之外,任何人都可以帮助我理解什么是有界类型参数列表

List<E extends Number> c;

答案 1

以下是这三者的摘要:

  • List:没有类型参数的列表。它是一个列表,其元素是任何类型 - 元素可以是不同类型的

  • List<?>:具有无界类型参数的列表。其元素属于特定但未知的类型;元素必须都是相同的类型

  • List<T extends E>:具有名为 . 的类型参数的列表。提供的 type 必须是扩展的类型,或者它不是参数的有效类型。TTE


答案 2

你应该真正看看有效的Java,第23项:不要在新代码中使用原始类型。

要使用该书中的示例,请考虑以下示例...如果你有一个集合,你不关心其中的元素类型,该怎么办?例如,您希望查看两个集合之间有多少个共同的元素。您可能会想到以下内容:

public static int numElementsInCommon(Set s1, Set s2) {
  int result = 0;
  for (Object o : s1) {
    if (s2.contains(o)) {
      ++result;
    }
  }
  return result;
}

此示例虽然有效,但由于使用了原始类型,因此不是一个好主意。原始类型根本不是类型安全的...您最终可能会以非类型安全的方式修改集合并损坏程序。相反,请谨慎行事,并使用类型安全的替代方法:

public static int numElementsInCommon(Set<?> s1, Set<?> s2) {
  int result = 0;
  for (Object o : s1) {
    if (s2.contains(o)) {
      ++result;
    }
  }
  return result;
}

不同之处在于,您只能添加到 中,并且不能对从 中取出的元素进行任何假设。如果您使用 raw ,则可以向其添加任何您想要的内容。该方法是一个很好的例子,您甚至不需要添加任何内容,也不需要对集合中的内容进行任何假设。这就是为什么它是使用通配符的良好候选者。nullSet<?>Set<?>SetnumElementsInCommon?

希望这有帮助。在有效的Java中阅读整个项目,它将真正变得清晰。

要回答问题的第二部分...还记得我说过当你使用通配符时,你不能对你从集合中取出的元素做任何假设吗?如果您确实需要对从集合中删除的对象的接口进行假设,该怎么办?例如,假设您要跟踪一组内容。?Cool

public interface Cool {
  // Reports why the object is cool
  void cool();
}

然后,您可能有一些这样的代码:

public static void reportCoolness(Set s) {
  for (Object item : s) {
    Cool coolItem = (Cool) item;
    coolItem.cool();
  }
}

这不是类型安全...你需要确保你传递了一个只有对象的集合。要修复它,你可以说:Cool

public static void reportCoolness(Set<Cool> s) {
  for (Cool coolItem : s) {
    coolItem.cool();
  }
}

这太棒了!完全符合您的要求,并且类型安全。但是,如果以后你有这个:

public interface ReallyCool extends Cool {
  // Reports why the object is beyond cool
  void reallyCool();
}

由于所有对象都是 ,您应该能够执行以下操作:ReallyCoolCool

Set<ReallyCool> s = new HashSet<ReallyCool>();
// populate s
reportCoolness(s);

但是你不能这样做,因为泛型具有以下属性:假设是 的子类,那么不是 的子类。这方面的技术讨论是“泛型类型是不变的”。(与协变相反)。BASet<B>Set<A>

要使最后一个示例正常工作,您需要创建一个通过强制转换(安全地)在 .为了避免让 API 的客户端通过这些令人讨厌的、不必要的代码,你可以让方法更灵活,如下所示:Set<Cool>Set<ReallyCool>reportCoolness

public static void reportCoolness(Set<? extends Cool> s) {
  for (Cool coolItem : s) {
    coolItem.cool();
  }
}

现在,您的方法采用任何包含 的元素或 的任何子类。所有这些类型都符合api...因此,我们可以安全地在任何元素上调用该方法SetCoolCoolCoolcool()

有意义?希望这有帮助。