自Java5 +钻石运算符以来初始化通用集合的方法

时间:2011-11-04 14:01:42

标签: java generics java-ee collections scjp

据我所知,泛型只在编译时有用。

因此可以声明:

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

然后,在这个字符串hashset中,添加狗或任何东西到那个集合,没有任何问题,因为在运行时没有任何检查,如数组(ArrayStoreException ...)(但你可能遇到问题,如类播你用那套......

所以我想说的是,我们通常以这样的方式实例化通用集合:

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

我的问题是,为什么我们设置HashSet的类型,因为只有变量引用的类型才真正重要(我认为)。

我的意思是,我们可以简单地写一下:

Set<String> set = new HashSet();

它会完全相同吗? 那么为什么我们通常在实例化期间编写类型呢? (这不是强制性的)


修改

我知道类型推断的“钻石操作员”,但为什么我们甚至需要它!因为类型推断已经起作用了!

以下代码:

            Set<String> set = new HashSet();
            set.add("Test add a string 1");
            set.add("Test add a string 2");
            for ( String s : set ) {
                    System.out.println(s);
            }

产生输出:

  

测试添加字符串1   测试添加字符串2

自己测试一下 http://ideone.com/vuiCE

所以现在你谈论类型推断作为Java7特性,但它已经为我工作了......

使用java7我将不得不替换我的

            Set<String> set = new HashSet();

通过

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

对于完全相同的事情,还有2个额外的字符吗? (除非泛型不只是用Java7编译时间?我不知道)

5 个答案:

答案 0 :(得分:4)

  

它的工作方式完全相同吗?

在运行时:

在编译时:

您的代码会将包含任意对象的集合分配给存储包含字符串的集合的变量,该变量很糟糕,并且类型检查器会不满意

当您撰写new HashSet()时,您正在使用raw type。我提到这个问题是关于它的含义,以及为什么要避免它:


一般来说,你可以这样说:

Set<String> set = new HashSet();

不远
HashSet tmp = new HashSet();
...
Set<String> set = tmp;

反过来离

不远
HashSet tmp = new HashSet();
tmp.add(7);
...
Set<String> set = tmp;

哪些仍然可以编译,但有一千个原因是坏的:)


Java 7允许你编写

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

但是,它告诉编译器推断 < ... >之间的类型。

“钻石运营商”的文档:

答案 1 :(得分:3)

你最后的建议是类型推断,并通过所谓的钻石运营商在Java 7中引入:

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

所以是的,那可能从一开始就在那里。但这是Java,通常需要几十年的时间才能获得新的语言功能(&lt; - 略微夸张)

答案 2 :(得分:1)

尽管Java目前正在删除泛型类型信息,但他们一直非常谨慎地设计语言和库,以便将来可以添加完整的泛型类型信息(所谓的具体化)。任何正确编写的通用代码(即没有任何javac警告)将在语言更改后继续存在。在您的示例中,钻石版本是未来证据;原始版本不是 - 当然它仍然必须编译,但它会在运行时抛出类型转换异常。

然而,这种未来的兼容性完全是妄想。擦除将永远留在我们身边。如果引入完全具体化,可以肯定地说几乎所有非平凡的通用代码都会破坏,因为每个人都在编写不正确的通用代码。

这不是我们的错。 Java迫使我们去做。它给了我们废话,我们别无选择,只能破解它来实现东西。我们的代码充满了对擦除细节知识的依赖。

JDK是最有罪的。 JDK API经过精心设计,可以在物化后继续存在;但是实现依赖于擦除。如果Java添加了reification,则必须重写这些JDK代码。 Java团队如果责备我们编写不正确的代码而他们必须自己做同样的事情,那将非常虚伪。

考虑到在没有重写的情况下无法在没有重新生成的情况下存活的代码量,我们可以肯定地说Java永远不会添加具体化,只要它保持向后兼容性的承诺。

在那种情况下,是的,无论如何。 Set<String> set = new HashSet()没什么可怕的。就个人而言,我总是添加<>以避免警告。 (实际上,只要有可能,我会尝试使我的代码具有防御性,就像我说的那样,这是一种妄想行为)

在您的示例中,推理是微不足道的。可能有更复杂的情况,<>推断不是那么简单,它的类型检查可能有用,原始版本缺乏。

class Foo<T>
{
    Foo(Class<T> clazz){}
}

Foo<String> foo1 = new Foo(Integer.class);    // compiles (wrongly) 

Foo<String> foo2 = new Foo<>(Integer.class);  // does not compile

答案 3 :(得分:0)

Java 7解决了有限类型推断的增加 类实例创建表达式。在需要为a显式声明参数化类型的情况下 构造函数和完整的参数化类型&lt; T1,T2,...... Tn>的 那个构造函数从上下文中显而易见,然后是参数化的 构造函数的类型可以替换为空的类型集 参数:&lt;&gt;。 &lt;&gt;构造是合法的,在构建时使用 对象并将其分配给变量,或者将其传递给变量 一个参数。

详细信息:click here

答案 4 :(得分:0)

实际上并不是必需的。 如果您输入

Set<String> s = new HashSet();

代码编译,我们得到一个警告,我们可以用Xlint检查。

Note: filetest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

然后,如果我们尝试添加一个整数值,例如我们得到一个编译错误。