Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala traits and structural types: can a trait extend a structural type and then call super?

Tags:

scala

I'd like to have a trait that can a) be mixed into any class with a particular method, and b) can call super. Something like this:

  // A and B are from a library that I don't control.  No changes allowed here.
  class A {
    def stuff = "a stuff"
  }
  class B {
    def stuff = "b stuff"
  }

  // My code starts here

  type HasStuffMethod = {
    def stuff: String
  }

  // Note that this doesn't compile - gets:
  //   class type required but AnyRef{def stuff: String} found
  trait ImplementsStuff extends HasStuffMethod {
    override def stuff = "trait + " + super.stuff
  }

  val a = new A with ImplementsStuff
  assert(a.stuff == "trait + a stuff")

  val b = new B with ImplementsStuff
  assert(b.stuff == "trait + b stuff")

Is there any way to do this?

Note that I don't control A and B; they're coming from another library that I can't modify.

[Edit - added after seeing answers]

Is there a way to call the original method in something like this?

  trait ImplementsStuff {
    this: HasStuffMethod =>
    abstract override def stuff = "foo" + "how do I call the original method here?"
  }

This isn't useful, since when you mix it into something it gives:

error: overriding method stuff in class A of type => java.lang.String; method stuff in trait ImplementsStuff of type => java.lang.String cannot override a concrete member without a third member that's overridden by both (this rule is designed to prevent ``accidental overrides'')

But it's no accident; yes, I really do want you to step all over that existing method. And then let me call it, too.

like image 643
James Moore Avatar asked Mar 02 '12 18:03

James Moore


People also ask

Can a trait extend a class Scala?

Yes they can, a trait that extends a class puts a restriction on what classes can extend that trait - namely, all classes that mix-in that trait must extend that class .

What are traits used for in Scala?

Traits are used to share interfaces and fields between classes. They are similar to Java 8's interfaces. Classes and objects can extend traits, but traits cannot be instantiated and therefore have no parameters.

What is a Scala trait when do you use Scala traits?

Traits are used to define object types by specifying the signature of the supported methods. Scala also allows traits to be partially implemented but traits may not have constructor parameters. A trait definition looks just like a class definition except that it uses the keyword trait.

Can traits have implementation Scala?

In Scala, we are allowed to implement the method(only abstract methods) in traits. If a trait contains method implementation, then the class which extends this trait need not implement the method which already implemented in a trait.


2 Answers

I can think of two options:

option 1, using a self type annotation and calling stuff from a new method (that I imaginatively called callStuff) instead of overriding it.

  trait ImplementsStuff  {
    this: HasStuffMethod =>
    def callStuff = "trait + " + this.stuff
  }

  val a = new A with ImplementsStuff
  assert(a.callStuff == "trait + a stuff")

  val b = new B with ImplementsStuff
  assert(b.callStuff == "trait + b stuff")

option 2 (since you say you don't control A and B) is the dear old decorator pattern.

  trait HasStuff { def stuff: String }

  class DecorateStuff(decorated: HasStuffMethod) extends HasStuff {
    def stuff = "trait + " + decorated.stuff
  }
  val decA = new DecorateStuff(new A)
  assert(decA.stuff == "trait + a stuff")

  val decB = new DecorateStuff(new B)
  assert(decB.stuff == "trait + b stuff")
like image 155
Paolo Falabella Avatar answered Oct 14 '22 06:10

Paolo Falabella


You need an abstract override in such a case.

like image 23
Daniel C. Sobral Avatar answered Oct 14 '22 08:10

Daniel C. Sobral