2016-05-30 191 views
0

项目23(不要使用新代码的原始类型)有效的Java中声称,在Java代码中使用原始类型总是危险原始类型总是不好?

例如,它声称,下面的方法是危险和不安全

// Use of raw type for unknown element type - don't do this! 
static int numElementsInCommon(Set s1, Set s2) { 
    int result = 0; 
    for (Object o1 : s1) 
     if (s2.contains(o1)) 
      result++; 
    return result; 
} 

尽管我可以看到在s1和s2上执行任何类型的写操作或类转换操作都会导致各种异常,但我不明白为什么上述方法不安全。作者Joshua Bloch继续推荐以下方法。

// Unbounded wildcard type - typesafe and flexible 
static int numElementsInCommon(Set<?> s1, Set<?> s2){ 
    int result = 0; 
    for (Object o1 : s1) 
    if (s2.contains(o1)) 
     result++; 
    return result; 
} 

换句话说,如果一个方法只使用Object.Class的方法和没有做任何修改所传递的参数,为什么这样做不好?

+3

在很久以前,在泛型之前,人们会在当天的集合中混合**类型(即'Hashtable'和'Vector',它们不是那些日子里的集合)。这导致了诸如'[0,“abc”,Date]'这样的事情,因为Java允许你创建自己的数据类型。使用'Set ',您可以保证一致的元素类型。 –

+0

当然。我的问题是,原始类型不安全的情况下,只读方法或操作只涉及暴露的方法** Object.Class ** –

+0

不,它们不是。在运行时,所有类型都是原始的,并且它们与推荐的通配符方法完全相同。但是这种方法会让编译器检查你实际上没有做这些“安全的事情”。如果没有通配符,如果您不小心将其添加到您的套件中,则不会收到错误消息。因此,如果您知道它,请使用通用类型;如果不知道,请使用通配符。不要使用原始类型。 – Thilo

回答

2

从某种意义上说,它阻止编译器对类型检查代码进行类型检查是不好的。

当然,你可以在没有它的情况下编写出好而安全的代码。人们每天都用Perl和Javascript来做。即使在Java中,使用原始类型和在运行时使用类型集合也没有区别。但是为了尽可能多地进行编译器检查,有一点可以说。

因此,采用类似于您的示例的方法,请使用通配符类型(Set<?>)而不是原始类型。这样,如果你确实写了“危险”代码,如s1.addAll(s2);,编译器会向你大喊。

+0

有道理。我很惊讶地发现界面上的捕获限制在孩子班级中没有实施。例如,请参阅https://gist.github.com/dsKarthick/e6b1b62c70f3722b13d9047aa338b8c2。 –

+0

我想你会得到一个关于子类中原始类型的编译器警告。如果你使用类型,他们必须匹配父界面。 – Thilo

+0

甚至允许您在子类(或Java中的任何位置)上使用原始类型的原因是与Java5之前的旧代码的向后兼容性。但是,如果您使用类型,则会正确执行。 – Thilo