我知道Liskov替代原则。
但我仍然对以下代码有疑问:
下面的代码有父类A和子类B. {B}子类中重写了testException
方法,因此它不会抛出任何已检查的异常。
public class A {
public void testException() throws IOException{
//some code with IO
}
}
public class B extends A {
public void testException() {
//no IO code
}
}
public class Test {
public static void main(String[] args) {
first:
try {// compilation error
B b = new B();
b.testException();
} catch (IOException e) {}
second:
try {
A a = new B();// Why does not cause compilation error?
a.testException();
} catch (IOException e) {}
}
}
在Test类中,标记为first
的try-catch块不会编译。但第二块没有任何问题。那是为什么?
更新:我认为第二个块只是因为运行时多态性而编译,类型A
的对象a可以在运行时指向任何子类,编译器也不会对它有任何线索,对吧?
答案 0 :(得分:1)
您的错误来自于B类中的testException方法不会引发IO异常,因此您的try / catch块中不会捕获可能的异常。
答案 1 :(得分:1)
我认为第二个块只是因为运行时多态性而编译,A类型的对象在运行时可以指向任何子类,而编译器也不会对它有任何线索,对吧?
是的,就是这样。在first
块中,我们确定b
B b = ...
b.testException();
的类型为B
,这意味着它可以包含类B
或其子类的实例。由于编译器知道类testException()
(或其子类)中的B
方法永远不会抛出任何IOException
(子类不能向重写方法添加新的已检查异常),因此它会通知您试图做一些不必要的事情(创建死代码 - 永远不会被执行的代码),在这种情况下处理异常,这里没有机会抛出。
这种情况在second
阻止的情况下有所不同,因为我们有
A a = ....
a.testException();
此处编译器无法确定a
将保留哪个对象(至少在其当前版本中不会,可能在将来此行为将得到改进)。因此虽然它可能包含类B
的实例,但它也可能是类A
的实例。因此,testException
可能会抛出IOException
,并且编译器没有任何理由阻止您处理它(由于这种可能性,实际上处理它是强制性的。)
答案 2 :(得分:0)
A a = new B();
a.testException();
编译器将在此处请求try-catch
块或throws
子句,因为多态性是运行时现象,编译器在编译时解析方法调用。
因此,它看到testException()
中的A
会引发IOException
因此请求try-catch
阻止或throws
条款。