I read the section of Programming in Scala where abstract override
is introduced, but I'm still confused by what exactly is signified by the joining of these modifiers. The snippet of code in which these modifiers is used is pasted below:
trait Doubling extends IntQueue {
abstract override def put(x: Int) { super.put(2 * x) }
}
In particular, I am confused by the purpose of abstract
in this case, and why we cannot achieve the expected results simply with the override
keyword. If we did not include a call to super
, would we need the keyword abstract
? Why or why not? I'm looking for a detailed explanation of this keyword combo as it pertains to stackable traits.
A part of late binding in scala traits posts; provides a very clear explanation; provided verbatim bellow (read the full post for more info):
The abstract base class provided an implementation of the
requestApproval
method. This is good since the leftmost trait calls this method. What happens if the base class’s method is abstract?If we change this, we get a rather odd message from the compiler: error: method
requestApproval
in classApprovalRequest
is accessed from super. It may not be abstract unless it is overridden by a member declaredabstract
andoverride
The combination ofabstract
andoverride
tells the compiler that the final implementation of the method will be provided by the class mixing-in the trait. If we add the abstract keyword to the methods, we can no longer use our anonymous implementation ofApprovalRequest
. That object can’t be created since the abstract override methods will be looking for an implementation ofrequestApproval
and there isn’t one. Instead we have to create a new class that extendsApprovalRequest
and implementsrequestApproval
. We then mix the traits into an instance of that class.Which will now give the output:
The reason is that the base class method is abstract
If you were to not put
abstract
on the trait you end up with the explanation you were seeking:So - you would need to mark the method as
abstract
.Here is the "other side" of the equation: if the methods do have implementations then it is not necessary to mark the
trait
's method asabstract
:It is now unnecessary to include
abstract
The idea is that it's an incomplete override -- you still want to require the eventually concrete implementation of the trait to provide that method, even though you're modifying that hypothetical method's behavior. In other words, the method you're overriding isn't a full standalone implementation. It gives a similar effect as a method decorator might in Python.
As far as I can reason, a method on a trait is
abstract override
if and only if it callssuper
, but it breaks encapsulation to expect the client of the code to inspect the implementation of the method to know it needs a concrete implementation. Therefore, you must mark itabstract override
to fully define the interface.