tags:

views:

113

answers:

3

I have a base class that comes from a Java library, whose code I cannot modify. This class (A) has an empty method (b) which should have been declared as abstract instead:

class A {
  def b { }
}

I extend this class in Scala and override the method to make it abstract:

abstract class AA extends A {
  override def b
}

Now I implement this method in a trait:

trait B {
  def b { println("B") }
}

If I extend AA with trait B, I get a error: overriding method b in class A of type => Unit; method b in trait B of type => Unit needs `override' modifier:

class C extends AA with B {}

Instead, if the code had been like this, everything is compiled without errors, which seems a bit contradictory to me:

abstract class AA {
  def b
}

trait B {
  def b { println("B") }
}

class C extends AA with B {}

I'm running Scala 2.8.0RC3, and completely new to the language (3 days). Another weird and related behaviour is that the override label is not necessary when making b abstract:

abstract class AA extends A {
  def b
}
+1  A: 

Not sure if this is the right solution, but if your trait B extends A (and override b), then everything compile fine:

First let's define A and AA like you present them in your question:

C:\Users\VonC>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class A {
     | def b { println("A b") }
     | }
defined class A

scala> new A
res5: A = A@153bedc4

scala> res5.b
A b

scala> abstract class AA extends A {
     | override def b
     | }
defined class AA

What you did:

scala> trait B {
     | override def b { println("B b") }
     | }
<console>:6: error: method b overrides nothing
       override def b { println("B b") }
                    ^

What I tried with trait B (in order to be able to add the 'override'):

scala> trait B extends A {
     | override def b { println("B b") }
     | }
defined trait B

So now:

scala> class C extends AA with B {}
defined class C

scala> new C
res7: C = C@1497b7b1

scala> res7.b
B b

The right b overriden method is called with C.b


As for your apparent "inconsistency", see Scala for Java Refugees Part 5: Traits and Types:

To start with, there’s that ever-annoying override keyword. I mentioned back in the article on basic OOP that any method which overrides a method in a superclass must be declared with the override modifier. At the time, I likened it to the language mandating the use of the @Override annotation, with its primary purpose being to enforce the good practice.

The real key to the power of traits is the way in which the compiler treats them in an inheriting class.
Traits are actually mixins, not true parent classes.
Any non-abstract trait members are actually included in the inheriting class, as in physically part of the class. Well, not physically, but you get the picture.
It’s as if the compiler performs a cut-and-paste with the non-abstract members and inserts them into the inheriting class. This means that there’s no ambiguity in the inheritance path, meaning no diamond problem.

So no need for override keyword in your second example.

VonC
Thanks for your detailed reply. I think I understand traits as I come from Ruby, which has modules, sort of the same thing.IMHO making trait B extend A defeats the purpose of traits, as I cannot use this trait in another class hierarchy, which is precisely what I'm trying to do. Any ideas?I think the problem lies in Scala compiler, which is not seeing that method b has become abstract, and there's no ambiguity in its definition when mixing in the trait in the leaf class C.
A.R
@A.R: I understand; my answer was inspired by http://blog.objectmentor.com/articles/2008/09/29/a-scala-style-_with_-construct-for-ruby. Still checking for other solutions.
VonC
+3  A: 

To try to see what's going on, I tried this:

scala> class A{
     |   def b{ }
     | }
defined class A

scala> abstract class AA extends A{
     |   override def b
     | }
defined class AA

scala> class AAA extends AA{
     |   def b = println("AAA")
     | }
<console>:8: error: overriding method b in class A of type => Unit;
 method b needs `override' modifier
         def b = println("AAA")
             ^

Apparently, the source of the problem is that abstract classes can't "free up" methods in their superclass from the need for the abstract class's subclasses to include the 'override' modifier.

MJP
Well, the override thing can be fixed easily by undoing the ambiguity for the compiler with override def b = super[B].b in class C, but this shouldn't be necessary. That's my point. Thanks!
A.R
MJP is right: concrete members always can never be made abstract in a subclass. An abstract member declaration in a subclass has no effect. This is a consequence of the rules for mixin composition, where concrete always takes precedence over abstract, no matter what the order of the traits containing the definitions is.
Martin Odersky
+1  A: 

The problem is very subtle. As a rule of thumb, your class AA, which extends A, should be mixed with traits that also extend A.

You did:

class A {
  def b { }
}

abstract class AA extends A {
  override def b
}

trait B {
  def b { println("B") }
}

Hence when you mix AA and B method b is DEFINED twice. Once by A (Not overrided because the definition in B replaced the override in AA) and the second by B, the compiler can't chose one over the other because there is no hierarchy between the two (equaly named but unrelated) methods. If you want, think about it like this: The compiler "mixes" the bodies of AA and B; if he choses the method from AA it will be abstract, if he choses the method from B (What should happend), since it's not an override, your stuck with two methods b.

To solve this, you want to make sure that both methods override the same method, in which case the the compiler will understand you are talking about the SAME method and will give priority to the last trait mixed.

Now, to override the method b in B, that class has also to inherit from A. So the canonical way to do this would be:

class A {
  def b { }
}

abstract class AA extends A {
  override def b
}

trait B extends A{
  def b { println("B") }
}

class C extends AA with B {}

Which compiles just fine.

Now, when you do:

abstract class AA {
  def b
}

trait B {
  def b { println("B") }
}

class C extends AA with B {}

it's clear that both methods are the same so the compiler knows he has to use the method from the trait.

Other solutions include:

  1. Make B override AA
  2. Make b in A abstract (But you didn't want that)

Again, the problem is very subtle but I hope I made it a little clearer. To get a better understandig read Scala's Stackable Trait Pattern.

Skuge
Thanks, your explanation was very clear.
A.R