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.