问题
If I have an abstract class with the following function -
abstract class A{
void foo(String s) throws Exception{
throw new Exception("exception!");
}
}
And then another class that extends the abstract class and implements its own version of foo -
class B extends A{
void foo(String s){
//do stuff that does *not* throw an exception
}
}
Will this create problems? Specifically in the following test case -
Collection<A> col = new Collection<A>();
B b = new B();
col.add(b);
for(A a : col){
a.foo();
}
I did some testing and nothing seems to have broken, but I don't understand why B's foo was called and not A's
回答1:
Because of Polymorphism
.
Since, at runtime the actual object's type in the Collection
is B
so, B.foo()
was called.
Basically, if you have a sub-type object assigned to a super-class reference the runtime polymorphism makes sure that the sub-type's version of an instance method gets called i.e. if it has been overridden of course. If not, the call falls back upon the super-class version.
What qualifies as a valid method override?
An overridden method must have
- the same method signature
- a covariant return type (a sub-type can be returned)
- must not throw broader checked exceptions (applies to your question and @Dgrin91's comment i.e. just because the overridden method took some risks (threw exceptions) doesn't mean that the overriding method should do the same; so, it may not throw any exceptions at all)
- must not use a less restrictive access modifier (can make protected to public but not private)
回答2:
This is not a problem - in fact, it is a common practice to throw an exception in a base class, where the functionality is not implemented, and then override the implementation with something that does not throw. Once you override a method, the method of the base class does not get called.
One downside to this is that the users would need to catch your exception if it happens to be of a checked (as opposed to "runtime") kind. A common solution is throwing an unchecked exception.
Of course if the only purpose to throw an exception is indicating that the functionality is not implemented, it's best to mark the corresponding method abstract
, and let Java compiler catch the possible violations.
回答3:
As noted by another poster, this behaviour happens because of Polymorphism.
You collection was declared to be of elements of A. B, declared to extend A, is-a A. This is corroborated by the fact that you can add elements of type B to the collection (which expects instances of A).
Your implementation of A.foo throws an exception and, if it had been called, would indeed throw it. B, on the other hand, overrides method foo, not throwing any exception. As the instances that you added to the collection was one of B, is B.foo that gets called. It changes nothing that your for loop declares the instances as being of type A (which is valid since B is-a A).
The behaviour you observed is the expected one.
For better understanding, you may want to create:
class C extends A {}
and add one C instance to the collection. Upon iterating C's foo will delegate to the parent class (A) and then throw an exception as expected.
回答4:
You can override methods without declaring throws. This is useful for callers who are using a concrete class for example someone that are using B class is not needed to try-catch cause this implementation of the method doesn't throw anything.
More detailed explanation is provided by Jon Skeet here: Inheritance , method signature , method overriding and throws clause
来源:https://stackoverflow.com/questions/17053063/java-abstract-classes-which-throw