How to call super method when overriding a method

2020-02-24 19:02发布

问题:

It would appear that it is possible to change the implementation of a method on a class with a trait such as follows:

trait Abstract { self: Result =>
    override def userRepr = "abstract"
}

abstract class Result {
    def userRepr: String = "wtv"
}

case class ValDefResult(name: String) extends Result {
    override def userRepr = name
}

val a = new ValDefResult("asd") with Abstract
a.userRepr

Live code is available here: http://www.scalakata.com/52534e2fe4b0b1a1c4daa436

But now I would like to call the previous or super implementation of the function such as follows:

trait Abstract { self: Result =>
    override def userRepr = "abstract" + self.userRepr
}

or

trait Abstract { self: Result =>
    override def userRepr = "abstract" + super.userRepr
}

However, none of these alternatives compile. Any idea how this could be accomplished?

回答1:

Here is the answer I was looking for. Thank you Shadowlands for pointing me in the right direction with Scala's abstract override feature.

trait Abstract extends Result {
    abstract override def userRepr = "abstract " + super.userRepr
}

abstract class Result {
    def userRepr: String = "wtv"
}

case class ValDefResult(name: String) extends Result {
    override def userRepr = name
}

val a = new ValDefResult("asd") with Abstract
a.userRepr

Live code is available here: http://www.scalakata.com/52536cc2e4b0b1a1c4daa4a4

Sorry for the confusing example code, I am writing a library that deals with the Scala AST and was not inspired enough to change the names.



回答2:

I don't know if you are in a position to make the following changes, but the effect you want can be achieved by introducing an extra trait (I'll call it Repr), and using abstract override in the Abstract trait:

trait Repr {
    def userRepr: String
}

abstract class Result extends Repr {
    def userRepr: String = "wtv"
}

case class ValDefResult(name: String) extends Result {
    override def userRepr = name
}

trait Abstract extends Repr { self: Result =>
    abstract override def userRepr = "abstract-" + super.userRepr // 'super.' works now
}

Your example usage now gives:

scala> val a = new ValDefResult("asd") with Abstract
a: ValDefResult with Abstract = ValDefResult(asd)

scala> a.userRepr
res3: String = abstract-asd


回答3:

abstract override is the mechanism, aka stackable traits. It's worth adding that linearization counts, because that's what determines what super means.

This question is a great addendum to the canonical Q&A on self-type vs extension.

Where the inheritance is ambiguous with self-types:

scala> trait Bar { def f: String = "bar" }
defined trait Bar

scala> trait Foo { _: Bar => override def f = "foo" }
defined trait Foo

scala> new Foo with Bar { }
<console>:44: error: <$anon: Foo with Bar> inherits conflicting members:
  method f in trait Foo of type => String  and
  method f in trait Bar of type => String
(Note: this can be resolved by declaring an override in <$anon: Foo with Bar>.)
              new Foo with Bar { }
                  ^

Then obviously, you can choose:

scala> new Foo with Bar { override def f = super.f }
res5: Foo with Bar = $anon$1@57a68215

scala> .f
res6: String = bar

scala> new Foo with Bar { override def f = super[Foo].f }
res7: Foo with Bar = $anon$1@17c40621

scala> .f
res8: String = foo

or

scala> new Bar with Foo {}
res9: Bar with Foo = $anon$1@374d9299

scala> .f
res10: String = foo