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)