When declaring an abstract type without a clear reason to prefer a
trait
, use anabstract class
.
Since in most scenarios that aren’t declaring an ADT, it’s impossible to guarantee that your type will never be involved in multiple inheritance, this rule can mostly be simplified to:
When writing an ADT, have the root type be an abstract class
Adding a new concrete method to an existing trait breaks binary compatibility, which is a big deal for libraries.
The reason this happens has been explained far better than I could hope to do.
Traits can be a bit painful when you’re writing Java code that has Scala dependencies. One concrete example of this is companion objects:
trait ATrait
object ATrait {
def foo(): Int = 1
}
abstract class AnAbtractClass
object AnAbstractClass {
def foo(): Int = 2
}
With these definitions, calling foo
from Java code will look like:
ATrait$.MODULE$.foo()
AnAbstractClass.foo()
Clearly, the abstract class version is more natural.
The previous reasons don’t really matter to projects that are not libraries or will never have to be used from Java code.
But abstract classes are not a worse choice than traits in most other scenarios (again, except when multiple inheritance is a possibility), and I prefer:
When in doubt, use an abstract class.
to
When in doubt and writing code that is or might become a library or is or might be called from Java, use an abstract class.