首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >无界通配符参数化类型的数组的实际用途是什么?

无界通配符参数化类型的数组的实际用途是什么?
EN

Stack Overflow用户
提问于 2013-08-19 09:03:26
回答 2查看 642关注 0票数 7

我正在通过AngelikaLangerParametrizedTypeWorkAround阅读。我理解这里的许多概念,我理解什么是无界的外卡参数化类型。虽然引用了其中的内容,但它指出:

代码语言:javascript
运行
复制
static void test() { 
  Pair<?,?>[] intPairArr = new Pair<?,?>[10] ;  
  addElements(intPairArr);  
  Pair<Integer,Integer> pair = intPairArr[1];  // error -1 
  Integer i = pair.getFirst();  
  pair.setSecond(i); 
} 
static void addElements(Object[] objArr) { 
  objArr[0] = new Pair<Integer,Integer>(0,0); 
  objArr[1] = new Pair<String,String>("","");    // should fail, but succeeds 
} 

对于无界通配符参数化类型,我们在如何使用数组元素方面受到额外限制,因为编译器阻止对无界通配符参数化类型的某些操作。本质上,原始类型和无界通配符参数化类型的数组在语义上与我们使用具体的通配符参数化类型的数组表示的非常不同。由于这个原因,它们不是一个很好的解决方法,只有在数组的优越效率(与集合相比)是最重要的时候才能被接受。

我这里有两个具体的问题。

  1. 无界外卡参数化类型的实际用途是什么?从示例中可以清楚地看出,您可以向数组中添加元素,但是在检索时,它会发出编译器错误?
  2. 这篇文章是什么意思,当它声明,只有当卓越的数组效率是最重要的时候,这些外卡参数化是可以接受的?

有人能在这个问题上详细阐述吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-08-19 09:22:38

首先,关于这个代码:

代码语言:javascript
运行
复制
static void addElements(Object[] objArr) { 
  objArr[0] = new Pair<Integer,Integer>(0,0); 
  objArr[1] = new Pair<String,String>("","");    // should fail, but succeeds 
}

在这里,您将Object[]类型的参数传递给addElements方法。因此编译器将允许您添加任何属于Object的内容。甚至这些代码也会编译:

代码语言:javascript
运行
复制
static void addElements(Object[] objArr) { 
  objArr[0] = new Pair<Integer,Integer>(0,0); 
  objArr[1] = new Pair<String,String>("","");
  objArr[2] = new Date(); // won't be a compilation error here
}

但是,您将获得运行时异常,因为一般类型是编译时检查和运行时转换。

现在你的问题是,为什么甚至允许在泛型中使用原始类型?

允许它向后兼容旧JVM的原因之一,也适用于接口开发人员可能不知道在运行时可以提供的所有类型的情况。您的error-1确实需要从原始类型转换为特定类型:

代码语言:javascript
运行
复制
// this should compile
@SuppressWarnings("unchecked")
Pair<Integer, Integer> pair = (Pair<Integer, Integer>) intPairArr[0];  // NO error -1

编辑:

关于通配符困境的

让我们以一个使用无界通配符的简单例子为例:

代码语言:javascript
运行
复制
Pair<?, ?> intPair = new Pair<Integer, Integer>(4, 9);
Object val2 = intPair.getSecond();
System.out.printf("val2: %d, isInt: %s%n", val2, (val2 instanceof Integer));
intPair.setFirst( null ); // assigning null will be allowed

它将编译和运行并产生预期的输出:

代码语言:javascript
运行
复制
val2: 9, isInt: true

然而,这将不会编译:

代码语言:javascript
运行
复制
intPair.setSecond((Object) new Integer(10)); // compile error
intPair.setSecond(new Integer(10)); // compile error

unbounded wildcard参数化类型(如Pair<?,?> )中,字段的类型和方法的返回类型将是unknown,即这两个字段都是?类型。setter方法将采用?类型的参数,getter方法将返回一个?

在这种情况下,编译器将不允许向字段分配任何内容,也不允许将任何内容传递给setter方法。原因是编译器无法确保我们试图作为set方法的参数传递的对象是预期类型,因为预期类型是未知的。

相反,可以调用getter方法并返回未知类型的对象,我们可以将该对象分配给类型对象的引用变量。

因此,您是正确的,在某种程度上,它确实限制了它的使用,正如上面所示,在构造期间可以分配值,但当您尝试调用setter方法时就不能这样做。

但是,您可以通过使用通配符和低绑定类型的来提高代码的有用性,如下所示:

代码语言:javascript
运行
复制
Pair<? super Object, ? super Object> intPair = new Pair<Object, Object>(4, 9);
Object val2 = intPair.getSecond();
System.out.printf("val2: %s, isInt: %s%n", val2, (val2 instanceof Integer));
intPair.setSecond(10);
val2 = intPair.getSecond();
System.out.printf("val2: %s, isInt: %s%n", val2, (val2 instanceof Integer));

现在,这不仅编译,而且运行预期的结果:

代码语言:javascript
运行
复制
val2: 9, isInt: true
val2: 10, isInt: true

关于你的第二个问题:我直接引用你的链接文章中的段落:

通过使用原始类型数组或无界通配符参数化类型,我们提供了静态类型检查,以确定同质序列。因此,我们必须使用显式强制转换,否则就可能出现意外的ClassCastException。在无界通配符参数化类型的情况下,我们在如何使用数组元素方面受到额外的限制,因为编译器阻止对无界通配符参数化类型的某些操作。本质上,原始类型和无界通配符参数化类型的数组在语义上与我们使用具体的通配符参数化类型的数组表示的非常不同。由于这个原因,它们不是一个很好的解决方法,只有在数组的优越效率(与集合相比)是最重要的时候才能被接受。

作者强调,数组中的无界通配符并不是一个很好的解决方法,因为它的限制和优越的效率仅在arrays vs collections的上下文中。

票数 4
EN

Stack Overflow用户

发布于 2013-08-19 09:24:33

当您需要灵活的泛型类型(例如接受Collection时)时,无界通配符基本上是语法糖,但实际上并不关心类型是什么(您只是打印它的toString)。你可以用一个通用的方法做同样的事情,但是它变得笨拙,不会给你买任何东西。一个无限制的通配符可以让你省去它。

关于数组的解释只是提醒您,在大多数情况下,最好使用集合类型而不是数组,除非您真的无法负担集合的额外成本。在您的示例中,您了解了原因:当您以Object[]的形式传入数组时,您告诉编译器,向该数组添加任何对象是完全可以的,而且由于Pair<Integer,Integer>是一个Object,所以编译器允许您这样做。根据您的其他代码(尚未完全触发异常),您现在可能在intPairArr中出现堆污染。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18310023

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档