Scala: How to inherit the same trait twice?

耗尽温柔 提交于 2019-12-11 01:54:06

问题


I'm following along in Odersky's "Programming in Scala" 2nd edition, and in section 12.5 "Traits as stackable modifications", he presents an IntQueue along with a trait that doubles any values you insert into the queue:

import scala.collection.mutable.ArrayBuffer
abstract class IntQueue {
 def get(): Int
 def put(x: Int)
}

class BasicIntQueue extends IntQueue {
 private val buf = new ArrayBuffer[Int]
 def get() = buf.remove(0)
 def put(x: Int) { buf += x }
}

trait Doubling extends IntQueue {
 abstract override def put(x: Int) {
  super.put(2 * x)
 }
}

The book then shows that you can instantiate a queue which doubles every integer you insert into it via new BasicIntQueue with Doubling. What I wanted to do was created a similar queue which multiplies every integer by 4, like this: new BasicIntQueue with Doubling with Doubling. However, this triggers a compile error "trait Doubling is inherited twice". Looking into this, I guess this has something to do with the limitations of linearlization; specifically that a given trait cannot appear twice in the linearlization of a class hierarchy.

What's the best way, then, to achieve the effect I want?

Here's a bit more information on my "real world" use case, in case the answer depends on this:

I have a class SoundFile, which reads a .wav file, and yields a SoundFile object, which extends a WaveForm trait. The SoundFile class is analogous to the BasicIntQueue above, and the WaveForm trait is analogous to the IntQueue above.

I have 2 traits that are analogous to Doubling, one called Echo and one called Reverse.

I wanted to write new SoundFile("myFile.wav") with Reverse with Echo with Reverse, but I ran into that same compile error about inheriting from the Reverse trait twice.


回答1:


Unfortunately you can't inherit from the same trait twice. Instead you should use some other mechanism. For instance, Reverse and Echo are both manipulations of the waveform. You could have

val reverse = (w: Waveform) => // produce a reverse waveform
val echo =    (w: Waveform) => // copy the waveform onto itself with delay
new SoundFile("myFile.wav", reverse andThen echo andThen reverse)

or somesuch.

If you require more changes than just a simple function, you'll have to encapsulate the modifications to functionality in your own class:

trait Transform { self =>
  def apply(w: Waveform): Waveform
  def time: Double
  def andThen(t: Transform) = new Transform {
    def apply(w: Waveform) = t(self(w))
    def time = self.time + t.time
  }
}
val reverse = new Transform { def time = 0.0; def apply ... }
val echo    = new Transform { def time = 1.0; def apply ... }
// Same deal after here


来源:https://stackoverflow.com/questions/17287395/scala-how-to-inherit-the-same-trait-twice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!