Observation
There are many Exception
subtypes that don't have any added state or behavior. For example, ClosedByInterruptException
code.
Question
Why is subtyping without adding state or behavior "acceptable" in the case of Exception
? From what I know, an interface
should be used when defining new behavior, and a class
should be used when implementing behavior or when state is added (which may cause behavior to be overriden). Other than Exception
types, would applying be acceptable?
Please don't use markers as an example.
Elaboration
Below is a design that is frowned upon:
class Dog extends Animal {
public Dog(String name) {
super(name);
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
//...assume methods that use String name are not overridable
}
For those who don't understand why this is frowned upon, see bottom of post.
For those who do understand, this would fall under the same category as ClosedByInterruptException
: the subtypes do not define new state or behavior. So why would exceptions like this exist if the design is frowned upon?
Although the exception is more descriptive, an extended description can be included via a String
message. Seeing how a SocketException
is thrown in the case of a socket being closed early, rather than SocketClosedException
or SocketNotConnectedException
, why does a newer API use the design in question?
For those wondering why the design is frowned upon
Looking at the Cat
and Dog
example, both would perform the exact same way, since no new behaviors were added, nor were behaviors overwritten. The only difference would come from type checking via instanceof
. If you use instanceof
, you are attempting to perform some kind of behavior based on the type, for example:
Animal animal = ...;
if(animal instanceof Dog) {
//perform some behavior
} else if(animal instanceof Cat) {
//perform some behavior
}
In a case like this, the behavior should be contained within the type by overriding a method, and the behavior should be triggered via polymorphism.