我正在刷新有关Java泛型的知识。因此,我转向了Oracle的优秀教程……并开始为我的同事编写一个演示文稿。我在本教程中遇到了有关通配符的部分,内容为:
考虑以下方法,printList:
public static void printList(List<Object> list) { ...
printList的目标是打印任何类型的列表,但未能实现该目标-
它仅打印Object实例的列表;它不能打印List<Integer>
,List<String>
,List<Double>
,等等,因为它们不是的亚型List<Object>
。要编写通用的printList方法,请使用List<?>
:public static void printList(List<?> list) {
我知道那是List<Object>
行不通的;但我将代码更改为
static <E> void printObjects(List<E> list) {
for (E e : list) {
System.out.println(e.toString());
}
}
...
List<Object> objects = Arrays.<Object>asList("1", "two");
printObjects(objects);
List<Integer> integers = Arrays.asList(3, 4);
printObjects(integers);
你猜怎么着; 使用List<E>
我可以打印不同类型的列表,没有任何问题。
长话短说:至少本教程表明, 需要 通配符来解决此问题;但如图所示,也可以通过这种方式解决。所以,我想念什么?
(旁注:已通过Java7测试;因此,也许这是Java5,Java6的问题;但是,另一方面,Oracle似乎在教程更新方面做得很好)
严格来说,使用通用方法的方法要比带有通配符的版本强大,因此,是的,您的方法也是可能的。但是,本教程 并未
声明使用通配符是唯一可行的解决方案,因此本教程也是正确的。
与通用方法相比,使用通配符可以获得的好处:您只需编写较少的代码,并且界面更“干净”,因为非通用方法更易于掌握。
为什么泛型方法比通配符方法更强大:为参数提供一个可以引用的名称。例如,考虑一种方法,该方法删除列表的第一个元素并将其添加到列表的后面。使用通用参数,我们可以执行以下操作:
static <T> boolean rotateOneElement(List<T> l){
return l.add(l.remove(0));
}
使用通配符,这是不可能的,因为l.remove(0)
会返回capture-1-of-?
,但l.add
需要capture-2-of-?
。即,编译器无法推断出的结果与预期的remove
类型相同add
。这与第一个示例相反,在第一个示例中,编译器可以推断出两者是同一类型T
。此代码无法编译:
static boolean rotateOneElement(List<?> l){
return l.add(l.remove(0)); //ERROR!
}
那么,如果要使用带有通配符的rotationOneElement方法,该方法比通用解决方案更容易使用,该怎么办?答案很简单:让通配符方法调用通用方法,然后它就会起作用:
// Private implementation
private static <T> boolean rotateOneElementImpl(List<T> l){
return l.add(l.remove(0));
}
//Public interface
static void rotateOneElement(List<?> l){
rotateOneElementImpl(l);
}
标准库在许多地方都使用此技巧。其中之一是IIRC,Collections.java
我正在刷新我关于Java泛型的知识。所以我转向甲骨文的优秀教程...并开始为我的同事准备一个演示文稿。我在教程中遇到了通配符部分,上面写着: 考虑以下方法,打印列表: printList的目标是打印任何类型的列表,但它无法实现该目标-它只打印对象实例的列表;无法打印列表 我明白那个
问题内容: 我试图理解Java泛型,它们似乎很难理解。例如,这很好… …就是这个… … 还有这个 … …但是不能编译: 有人可以用简单的语言解释发生了什么吗? 问题答案: 对于泛型类型,主要要了解的是它们不是协变的。 因此,尽管您可以这样做: 以下内容将无法编译: 这是为了避免您绕过通用类型的情况: 因此,一一讲解您的示例 1个 您的通用方法采用a ,而您采用;(基本上是)。可以分配给类型,并且编
在了解Java泛型的过程中,我遇到了以下问题: 假设我有下面的方法来添加列表的元素,只限于包含数字的列表。 但是这段代码和这段代码有什么不同: 它们都按预期编译和执行。这两者之间有什么区别?除了语法之外?什么时候我更喜欢使用通配符而不是前者? 是的,使用通配符方法,我不能在列表中添加除null之外的新元素,否则它将无法编译。除此之外呢?
问题内容: 我对Java中的通用通配符有两个疑问: 和之间有什么区别? 什么是有界通配符,什么是无界通配符? 问题答案: 在你的第一个问题中,并且是有界通配符的示例。无限制的通配符看起来像,基本上就是<? extends Object>。宽松地表示泛型可以是任何类型。有界通配符(或)通过说它必须扩展特定类型(称为上限)或必须是特定类型的祖先(称为下限)来对类型进行限制。
所以我在阅读泛型以重新熟悉这些概念,尤其是在涉及通配符的地方,因为我很少使用或遇到通配符。从我的阅读中,我不明白他们为什么使用通配符。下面是我经常遇到的一个例子。 你为什么不这样写: oracle网站上的另一个示例: 为什么这不是写成 我错过什么了吗?
问题内容: 我有一个接口将对象转换为字符串: 以及用于存储所有可用转换器的地图: 现在,我有了要转换的异构数据列表,如下所示: 但是此代码无法编译: 我应该如何更改代码? 问题答案: 您面临的问题称为通配符捕获。Java无法识别将从数据中接收的类型。尝试以两种方式中的任何一种重构代码 方法1:如下 更改您的界面 方法2: 通过类型推断来捕获通配符的Helper方法 创建如下的帮助方法, 如下调用此