I'm trying to come up with a complete answer to:
"why/when use an abstract class rather than an interface."
and looking for verification/suggestions on the following.
An answer to this is,
"to provide the implementation for some of it. Before the concrete classes
come in to define the specific types, an abstract class, typically right below
an interface in the inheritance hierarchy (as in many of its examples in the Java APIs)
implements and pins down some common aspects of the structure that interface defines.
Another good reason to use an abstract class is that there is a clear logical hierarchy among the types.
Abstract class has the use to organize that hierarchy
while forcing, by being an abstract class rather than a concrete one, the instantiation of objects only on the concrete classes
down below in this hierarchy where they are fully defined and thus are meaningful."
Then, in this context, comes the Q:
"since Java 8, interfaces can define default method implementations.
Why wouldn't i write default method(s) in the interface instead of an abstract class that implements those method(s)?
"
The answer to this would be:
"
One reason is: interface methods are all public, field members are all constants (final & public).
You may wanna restrict access privileges of methods and/or make them operate on non-constant state.
Another is: a descendant class can call upon the abstract class method(s) by super, while it can not do so on default interface methods. Also, the interface has no constructors to be invoked by the descendants.
The rest of the reasons are the same as those in pre-Java 8 above.
"
Other than these above, why would i rather go for an abstract class than an interface especially now that i have the default interface methods since Java 8?
Please note: i've seen Java 8 default methods vs. non-abstract methods in abstract classes and Interface with default methods vs Abstract class in Java 8 along with some other useful discussions. This Q is rather about why chose an abstract class over an interface than the other way round.
TIA.
解决方案
Here are some reasons to choose an abstract class over an interface. The ones you list - private fields, etc., are some of the major ones. These are a few more subtle ones
Type clarity. You can only extend one class. This makes it clearer what your object is an how to use it.
The diamond problem. You have to implement all of the default methods in an interface to help avoid the diamond problem. If the interface is Collections, this can be a huge pain since there are a billion of them. With an abstract class, you only overwrite what you need to
In Java 8, there are lambda expressions, and the old routine of passing around an interface with a method to implement has been usurped. Still, when you see an interface with a default method, this can interpreted in ways you might not intend
Here's what Oracle has to say on the subject:
Which should you use, abstract classes or interfaces?
Consider using abstract classes if any of these statements apply to
your situation:
You want to share code among several closely related classes.
You expect that classes that extend your abstract class have many common methods or fields, or require access modifiers other than
public (such as protected and private).
You want to declare non-static or non-final fields. This enables you to define methods that can access and modify the state of the
object to which they belong.
In this article, Orace defends the distinction between the two type systems
https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html
EDIT To clarify the "diamond problem" bullet
The problem is described here (and many other places)
http://www.lambdafaq.org/what-about-the-diamond-problem/
The problem occurs when you inherit from two places that declare the same method, and you have to pick one when resolving a function call. This was never an issue in Java 7-, since you could only extend one class and interfaces had no methods.
The resolution tactic now is a little complicated, but here is a good description of it:
(from http://blog.loxal.net/2013/05/java-8-default-interface.html)
To address the diamond problem there is a precedence in which order an
implementation is used: only if the class implements all default /
optional methods of its interfaces, the code can be compiled and the
implementations of this class are used. Otherwise the compiler tries
to patch the missing implementation(s) with interface's default
implementation. And if there are multiple default implementations of a
method, then the diamond problem occurs and the compiler rejects the
compilation. Also if the class implements an interface's default
method, the implementation of the class will be used instead of
interfaces's default implementation.
If you stick to abstract classes, you will never run into this problem. However, if your object is required to implement two interfaces (because it needs to be added to lists that are expecting those types, e.g.), and those interfaces have conflicting method signatures, you will wind up having to redefine a whole bunch of methods even if that means you're just making super calls, which sort of defeats the point of inheritance-based dynamic dispatch. It isn't a deal-breaker, but something to consider when structuring a complex Java project