Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/351.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 嵌套类型参数是如何工作的?_Java_Generics - Fatal编程技术网

Java 嵌套类型参数是如何工作的?

Java 嵌套类型参数是如何工作的?,java,generics,Java,Generics,为什么宣言 Set<Set<String>> var = new HashSet<Set<String>>(); Set<Set<String>> var = new HashSet<HashSet<String>>(); Set var=new HashSet(); 除了《宣言》之外的工作 Set<Set<String>> var = new HashSet<Se

为什么宣言

Set<Set<String>> var = new HashSet<Set<String>>();
Set<Set<String>> var = new HashSet<HashSet<String>>();
Set var=new HashSet();
除了《宣言》之外的工作

Set<Set<String>> var = new HashSet<Set<String>>();
Set<Set<String>> var = new HashSet<HashSet<String>>();
Set var=new HashSet();
窒息


我知道声明中的“顶层”(不确定这是否是正确的短语)泛型与尖括号中的泛型遵循不同的规则,但我有兴趣了解原因。对于谷歌来说,这不是一个容易的问题,所以我想我应该试试你们。

这是因为泛型在Java中的工作方式

Set<? extends Set<String>> var = new HashSet<HashSet<String>>();

Set这是因为如果允许,您可以绕过类型系统。你想要的财产叫做。如果集合是协变的,那么您可以这样做:

Set<Set<String>> var = new HashSet<HashSet<String>>();

var.add(new TreeSet<String>());

区别很简单,通过允许
Set var=new HashSet
您允许
var
只接受
Set
类型的值(其中
HashSet
属于
Set

至于
Set var=newhashset()
,它不仅不会编译,因为
var
的内部类型需要
Set
,而且它会找到一个
HashSet
。这意味着
var.add(newtreeset())将是有害的(在
HashSet
TreeSet
之间的类型不兼容)


希望这能有所帮助。

原因是
Set
不等同于
Set
Set
可以包含任何类型的
Set
,而
Set
只能包含
HashSet

如果
Set Set=new HashSet()
是合法的,您也可以这样做,而不会出现任何错误:

Set<HashSet<String>> setOfHashSets = new HashSet<HashSet<String>>();
Set<Set<String>> set = setOfHashSets;
set.add(new TreeSet<String>());
HashSet<String> wrong = set.iterator().next(); // ERROR!
如果我们在这里用
T
替换
Set
,我们得到
Set Set=newhashset()
。因此,很容易看出所涉及的实际类型参数是匹配的,赋值右侧的类型是左侧类型的子类型

Set<Set<String>> set = new HashSet<HashSet<String>>();
Set Set=newhashset();

这里我们必须分别用
T
S
替换
Set
HashSet
,其中
S
T
的一个子类型。完成后,我们得到
Set=newhashset()
。如上所述,
HashSet
不是
Set
的子类型,因此赋值是非法的。

让我们将示例简化为更简单的内容

//Array style valid an Integer is a Number
Number[] numArr = new Integer[10]
//Generic style invalid
List<Number> list1 = new ArrayList<Integer>();
//Compiled (erased) List valid, pre generics (java 1.4) 
List list2 = new ArrayList();
运行时异常不应该发生在有效代码中,但numArr只能保存整数,而不能像人们所相信的那样保存数字。泛型在编译时捕获此错误,因为它们不是协变的

这就是为什么这些数字和整数列表不能被接受为相同的原因。两个列表提供的方法都接受不同的参数,而整数列表仅接受整数更为有限。这意味着两个列表提供的接口不兼容

List<Number>.add(Number n)
List<Integer>.add(Integer n)
List.add(编号n)
List.add(整数n)
这同样适用于您的集合

Set<Set<String>>.add(Set<String> s)
HashSet<HashSet<String>>.add(HashSet<String> s)
Set.add(集合s)
add(HashSet s)
这两种类型的add和其他方法不兼容


(第二次尝试回答,希望这次我没有读过任何人)

这个答案对我来说没有多大意义。约翰·库格曼和科林确实清楚地说明了这个问题。这与“推断”类型参数没有任何关系。@erickson:它至少解决了这个问题,但在我发布它时没有,所以我留下它,但我把它作为最佳答案。@haylem:问题是,OP没有要求“解决”任何问题。。。他们问为什么会这样。这个答案相当于“它是那样工作的,因为它是那样工作的”,有些代码确实有效,但没有真正的解释。不过这不是一个解决方案。不是真的。您基本上不允许将任何内容添加到
var
。和@ColinD:if by“work”是指“compile”:-。
SetAnd,
Set
期望(if by“expects”是指
add()
方法允许)任何
Set
的实现者。Set甚至不是一个具体的类,所以如果一个
Set
没有采用
Set
的子类型,那么它就毫无用处了@Elite绅士:我不明白你更新的答案。您似乎建议代码将被编译,但再次指出它不会。然后,您建议添加
LinkedHashSet
会引发
ClassCastException
,而实际上添加
LinkedHashSet
会成功,因为
LinkedHashSet
HashSet
的子类型,即使没有,由于类型擦除,运行时在
add
方法中也没有任何检查。@Elite Gentle:我仍然不明白你的意思,添加
LinkedHashSet
的问题应该是什么。
HashSet
LinkedHashSet
之间没有任何类型不兼容,因为
LinkedHashSet
HashSet
。它只是没有真正说明所涉及的危险,因为它可以完美地工作。@ColinD,很好地发现了……我现在使用了
TreeSet
//this will compile but give us a nice RuntimeException 
numArr[0] = 1.5f
//This would compile and thanks to type erasure 
//would even run without exception
list1.add(1.5f)
List<Number>.add(Number n)
List<Integer>.add(Integer n)
Set<Set<String>>.add(Set<String> s)
HashSet<HashSet<String>>.add(HashSet<String> s)