简体   繁体   中英

Scala trait extending abstract class, how do I know whether an abstract method has been implemented or not

I am new to scala, and I have the following question

abstract class A {
    def foo(): List[String]
}

trait AA extends A {
    override def foo(): List[String] = {
        // Something like 
        // If super.foo is implemented, "AA" +: super.foo
        // If super.foo is not implemented, List("AA")
    }
}

class B extends A with AA {
    override def foo(): List[String] = {
        // I think the trait will have the implementation, so I should only need to do:
        super.foo
    }
}

Basically I would like each trait to add one part to the result of foo, so that I can have the final result by mixing multiple such traits. I think I can make the foo method in class A to return empty List, but I am just curious whether there is a way to check whether the method in parent has been implemented or not.

Also, please let me know if there is an anti-pattern.

I think you want the stackable trait pattern .

So you have an abstract class A which declares some method foo() , and you have a "decorator" of that method who says something like "I extend A and I would like to append 'AA' to whatever foo() returns".

abstract class A {
  def foo(): List[String]
}

trait AA extends A {
  abstract override def foo(): List[String] = "AA" :: super.foo()
}

Note the abstract override , this is the key. It allows us to append some behaviour to an abstract class.

Now let's say we do something like this:

class WithoutImpl extends A with AA {
  override def foo(): List[String] = {
    super.foo() // fails; needs "abstract override" or implementation
  }
}

This fails because everyone is decorating, but noone is actually implementing.

Let's add an implementation trait:

trait AAA extends A {
  override def foo(): List[String] = List("AAA")
}

Now we might do:

class WithImpl extends AA with AAA {
  def myFoo(): List[String] = {
    super.foo() // error! wrong order of initialization
  }
}

This will still fail due to the order of mixins. We must first provide an implementation, and then we provide the decorator(s), who will then keep adding behaviours.

class WithImpl extends AAA with AA  {
  def myFoo(): List[String] = {
    super.foo() // works!
  }
}

println((new WithImpl().myFoo())) // List("AA", "AAA")

You can add as many decorators as you wish, just pay attention to the order. Eg if we had BB and CC similarly to AA , we could do:

class WithImpl extends AAA with AA with BB with CC  {
  def myFoo(): List[String] = {
    super.foo() // List(CC, BB, AA, AAA)
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM