为什么Java在编译TestGenerics
class
时没有发出任何警告,因为String
class
是final
并且无法扩展?
import java.util.*;
public class TestGenerics {
public void addStrings(List<? extends String> list) {
// some code here
}
}
}
答案 0 :(得分:7)
假设我有一个这样的方法:
public List<? extends T> filterOutNulls(List<T> input) { ...
当然,这不是世界上最好的签名,但仍然完全合法。如果我将List<String>
传递给该方法会发生什么?根据签名,它返回List<? extends String>
。如果Java不允许使用该类型,则不可能将此方法用于List<String>
(或者至少,不可能使用返回值)。
其次,extends
语法在这种情况下仍然有用,因为List<String>
和List<? extends String>
有不同的限制 - 具体来说,除了{{1}之外,你不能添加任何内容文字到null
。我有时会使用List<? extends String>
来表示集合是只读的(因为您可以传入的唯一? extends
是T
),而null
表示写入 - 只有(因为你只能? super
作为T
)。这不是完全万无一失的(你仍然可以调用删除方法,传递Object
,传播,等等),但是可以温和地提醒你如何使用该集合。
答案 1 :(得分:6)
编译器并没有真正注意到这一事实,因为它并不重要。列表中仍然允许String
,在最终产品中,找不到任何扩展String
的可能性。在erasure之后,它出现如下:
public void addStrings(List list)
正如您所看到的,现在没有关于扩展String
的课程的建议。如果你创建了一个扩展String
的类,那将是一个编译错误。没有必要让javac担心这一点。
答案 2 :(得分:0)
类型系统不考虑List<String>
和List<? extends String>
等价,即使在编译时,String
没有自身以外的子类型,因此任何对象都是{{ 1}}也必须是List<? extends String>
。
一种解释是List<String>
不是最终的 - 可以从类中删除final
,但不应该破坏任何内容:http://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html#jls-13.4.2
类型系统考虑final
并不是没有先例;例如,我们不能将final
强制转换为String
,因为编译器认为如果对象是Runnable
,则它不能是实现String
的某个未知子类。 / p>
如果我们希望泛型也能做出类似的推理并推断Runnable
和List<String>
是等价的,那么打字规则会更加复杂。