3
votes

First of all, I realize that it doesn't make much sense to override a concrete method in superclass by an abstract method in subclass. But... since in Scala it is actually possible to do this, I tried the following snippet, and the result's getting me confused.

First scenario

  • The concrete method to be overridden in super-super-class
  • The abstract method is in super-class
class A {
  def x: String = "A"
}

abstract class B extends A {
  def x: String
}

class C extends B {
  def x: String = "C"
}

Executing the snippet above in scala REPL results in the follwing error:

  def x: String = "C"
      ^
<pastie>:10: error: `override` modifier required to override concrete member:
def x: String (defined in class A)

Now the question: Why it seems that the abstract method in class B was ignored? But B.x does actually have an effect if C.x is removed from definition. Because the following snippet doesn't compile either.

class A {
  def x: String = "A"
}

abstract class B extends A {
  def x: String
}

class C extends B

results in the following error

class C extends B
      ^
<pastie>:9: error: class C needs to be abstract. No implementation found in a subclass for deferred declaration
def x: String (defined in class B)

Second scenario

  • The concrete method to be overridden in super-class
  • The abstract method is in trait
class A {
  def x: String = "A"
}

trait B {
  def x: String
}

class C extends A with B

Try instantiate C,

scala> (new C).x
val res0: String = A

It looks like the B.x abstract method just got ignored by compiler.


Update

In the first edition of my question, I idiotically forgot to extends A in the second scenario, which leads to an incorrect conclusion that class and trait behave differently in my examples. I sincerely apologize for my negligence.

Let me try to rephase my question:

In both the first and second scenario, what is the effect of the abstract B.x in the middle of class hierarchy?

As I understand it, by inheritance and method resolution order (MRO),

  • in the first scenario B.x overrides A.x and C.x overrides B.x. Since B.x is abstract, when C.x implements B.x, it need not specify override modifier.
  • in the second scenario, B.x overrides A.x and C didn't implement the abstract B.x. So C should be abstract and does not compile.

But it seems to me that the compiler just ignored the abstract method B.x in the middle of class hierarchy. Is this behavior defined somewhere in the language specification, or this is totally unintended and unexpected (and just a compiler bug)?

1

1 Answers

1
votes

I'm not sure if the question is about the differences between Traits and Abstract Classes or about the differences between extends.

In the First Scenario you have abstract class B extends A in both cases but in the Second Scenario you have trait A and trait B. B is not extending A. In fact, if you put trait A extends B in the Second Scenario it won't compile

In the second case of the Second Scenario, you are extending 2 different traits that have the same method, having extends A with B your class has the method x defined because the order matters when you extend from multiple traits. If you try to do it in the opposite order extends B with A, it won't compile

In other words:

Second Scenario - First case: Defining an abstract method doesn't need the override

Second Scenario - Second case: C extends A with B is not the same as B extends A + C extends B