嵌套类型参数如何工作?

Big*_*ird 10 java generics

为什么声明

Set<Set<String>> var = new HashSet<Set<String>>();
Run Code Online (Sandbox Code Playgroud)

工作但宣言

Set<Set<String>> var = new HashSet<HashSet<String>>();
Run Code Online (Sandbox Code Playgroud)

呛?

我知道"顶级"(不确定这里是否是正确的短语)声明中的泛型通过与尖括号内的规则不同的规则,但我有兴趣了解原因.谷歌不是一个简单的问题,所以我想我会试试你们.

Joh*_*ica 12

这是因为如果允许的话,你可以绕过类型系统.您想要的属性称为协方差.如果集合是协变的,那么你就可以这样做:

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

var.add(new TreeSet<String>());
Run Code Online (Sandbox Code Playgroud)

TreeSet是一种Set类型,因此静态类型检查不会阻止您将TreeSet插入var.但var只需要HashSets和HashSets,而不是任何旧类型的Set.

就个人而言,我总是写下你的第一个声明:

Set<Set<String>> var = new HashSet<Set<String>>();
Run Code Online (Sandbox Code Playgroud)

外部类需要有一个conrete实现,但通常没有必要特别确定内部类到HashSet.如果你创建了一个HashSet of Sets,你就可以了.您是否继续在var中插入一系列HashSets是您在程序中稍后的选择,但无需限制声明.


对于它的价值,Java 中的数组协变的,与集合类不同.此代码将编译并将抛出运行时异常,而不是在编译时捕获.

// Arrays are covariant, assignment is permitted.
Object[] array = new String[] {"foo", "bar"};

// Runtime exception, can't convert Integer to String.
array[0] = new Integer(5);
Run Code Online (Sandbox Code Playgroud)


Col*_*inD 7

原因是Set<Set<String>>不等于Set<HashSet<String>>!A Set<Set<String>>可以包含任何类型Set<String>,而a Set<HashSet<String>>可以只包含HashSet<String>.

如果Set<Set<String>> set = new HashSet<HashSet<String>>()合法,您也可以没有任何错误地执行此操作:

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!
Run Code Online (Sandbox Code Playgroud)

但是,在这里使用有界通配符是合法的:

Set<? extends Set<String>> set = setOfHashSets;
Run Code Online (Sandbox Code Playgroud)

这是允许的,因为现在,集合包含的对象类型是? extends Set<String>......换句话说,"一些特定但未知的类实现Set<String>".由于您不确切知道Set<String>允许包含的具体类型是什么,因此不允许向其添加任何内容(除外null)...您可能错了,导致稍后出现错误,如我的第一个示例中所示.

编辑:

请注意,您在问题中引用的"顶级"泛型称为参数化类型,表示采用一个或多个类型参数的类型.其原因Set<Set<String>> set = new HashSet<Set<String>>()是法律是HashSet<T>工具Set<T>,因此是一个亚型Set<T>.但请注意,type参数T必须匹配!如果你有另一种类型S的子类型T,那么HashSet<S>(或者只是Set<S>偶数)不是子类型Set<T>!我解释了上述原因.

这正是这里的情况.

Set<Set<String>> set = new HashSet<Set<String>>();
Run Code Online (Sandbox Code Playgroud)

如果我们替换Set<String>T这里,我们得到Set<T> set = new HashSet<T>().那么,很容易看出,所涉及的实际类型参数匹配,并且赋值右侧的类型是左侧类型的子类型.

Set<Set<String>> set = new HashSet<HashSet<String>>();
Run Code Online (Sandbox Code Playgroud)

在这里,我们必须替换Set<String>和分别HashSet<String>使用T和的子类型.完成后,我们得到了.如上所述,它不是子类型,因此赋值是非法的.SSTSet<T> set = new HashSet<S>()HashSet<S>Set<T>