考虑一下片段:
Number[] numbers = {1, 2.3, 4.5f, 6000000000000000000L};
Run Code Online (Sandbox Code Playgroud)
完成上述操作Number是完全可以的,是一个抽象类.
继续前进
List<Long> listLong = new ArrayList<Long>();
listLong.add(Long.valueOf(10));
List<Number> listNumbers = listLong; // compiler error - LINE 3
listNumbers.add(Double.valueOf(1.23));
Run Code Online (Sandbox Code Playgroud)
曾3号线被设计成可以编译成功,我们最终会得到一个List的NumberS,即
for(Number num: listNumbers ){
System.out.println(num);
}
// 10
// 1.23
Run Code Online (Sandbox Code Playgroud)
这些都是数字.
我在一本书中看到了这个,
泛型不支持子类型,因为它会导致实现类型安全性的问题.这就是为什么
List<T>不被视为一个亚型List<S>,其中S是超级型T
如上所述,在这种特殊情况下哪种类型的安全性会丢失,第3行是否需要成功编译?
JB *_*zet 42
List<Long> listLong = new ArrayList<Long>();
List<Number> listNumbers = listLong;
Run Code Online (Sandbox Code Playgroud)
所以,listNumbers和listLong将是相同的列表两个引用,如果这是可能的,对不对?
listNumbers.add(Double.valueOf(1.23));
Run Code Online (Sandbox Code Playgroud)
因此,您可以在该列表中添加Double.listLong因此,类型List<Long>将包含Double.因此,类型安全将被打破.
如果是这样的话,那么我们可以添加其他不同亚型Number到listNumbers,必须禁止.
想象一下,你现在插入类型的对象Double和Long,后来又尝试使用Long#reverse.你的代码会编译,但当然会在运行时失败(坏),Double它将首先通过.
让我们使用一个非抽象基类的例子:
public class Human {
public string getName() {
// ...
}
}
public class Student extends Human {
public void learn(Subject subject) {
// ...
}
}
public class Teacher extends Human {
public void teach(Subject subject) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
在任何Human预期的地方,a Student或Teacher将会同样做,因为他们完全实现了Human界面.(在这种情况下,getName()可以在它们上调用它.)Java继承保证在技术上就是这种情况.使它在语义上工作是班级作者的工作,因此他的代码实现了Liskov替换原则.
所以并不这意味着我们也可以替代Collection<Teacher>其中Collection<Human>预期?不总是.请考虑以下方法:
public class Human {
// ...
public void join(Set<Human> party) {
party.add(this);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果Java允许将a Set<Student>作为聚会传递,那么非Student Humans加入该聚会的任何尝试都必须在运行时失败.
作为一般规则,如果接收者(在函数参数的情况下为被调用者,在函数返回值的情况下为调用者)想要将某些内容放入其中,则子类型的容器是不合适的,但如果接收者仅想要获取内容则可接受出来并使用它.如果接收器想要取出并使用它,那么超类型的容器是不合适的,但如果接收器只是将东西放入其中,则是可接受的.因此,如果接收器从集合中取出东西并将东西放入集合中,它们通常必须要求固定类型的集合.
我们的join方法只将Humans放入party,所以我们也可以允许Set<Object>a或非泛型Set或等价a Set<?>.Java允许我们使用下限通配符来实现这一点:
public class Human {
// ...
public void join(Set<? super Human> party) {
party.add(this);
}
}
Run Code Online (Sandbox Code Playgroud)
为了打开子类的可能性,有上边界的通配符:
public class Teacher extends Human {
public void teach(Subject subject, Set<? extends Student> schoolClass) {
for (Student student : class) {
student.learn(subject);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我们是子类Student,那么传递的schoolClass也可以是Set该子类型的一个.
| 归档时间: |
|
| 查看次数: |
1140 次 |
| 最近记录: |