2013-06-25 2 views
2

Я следую за собой в «Программировании в Scala» Одерский в 2-е издания, а в разделе 12.5 «Признаки как наращиваемые изменения», он представляет IntQueue наряду с чертой, которая удваивает любые значения, которые вы вставляете в очередь:Scala: Как наследовать одну и ту же черту дважды?

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) 
} 
} 

Книга затем показывает, что вы можете создать экземпляр очереди, которая удваивает каждое целое число, которое вы вставляете в него через new BasicIntQueue with Doubling. То, что я хотел сделать, было создано аналогичной очереди, которая умножает каждое целое число на 4, например: new BasicIntQueue with Doubling with Doubling. Однако это вызывает ошибку компиляции «trait Doubling is inherited twice». Взглянув в это, я полагаю, что это имеет какое-то отношение к ограничениям линейности; в частности, что данный признак не может появляться дважды в линейной иерархии классов.

Каков наилучший способ для достижения эффекта, который я хочу?

Вот немного больше информации о моем «реальном мире» прецеденте, в случае ответ зависит от этого:

У меня есть класс SoundFile, который считывает файл .wav, и дает SoundFile объект, который расширяет черту WaveForm. Класс SoundFile аналогичен приведенному выше значению BasicIntQueue, а признак WaveForm аналогичен приведенному выше IntQueue.

У меня есть 2 элемента, которые аналогичны Doubling, один из которых называется Echo, а один - Reverse.

Я хотел написать new SoundFile("myFile.wav") with Reverse with Echo with Reverse, но я столкнулся с той же ошибкой компиляции о наследовании от черты Reverse дважды.

+0

Есть ли у JVM это ограничение? Java делает; ни один класс не может реализовать один и тот же интерфейс дважды. –

+0

Я бы не удивился, если бы у JVM было это ограничение, но, не зная подробностей о том, как Scala реализует множество функций на JVM, я не понимаю, почему Scala не может генерировать байт-код, который «делает правильную вещь», , E.g. Если вы спросите, был ли этот анонимный класс экземпляром 'Doubling', он ответил бы' true', потому что это правда; не нужно делать тот факт, что он наследует два раза в системе типов. –

+0

Интересный вопрос. Подчеркивает, что черты менее «слои» и больше «mixin». Черты не являются обертками, не являются «функцией, применяемой к классу/интерфейсу для создания нового класса/интерфейса». –

ответ

3

К сожалению, вы не можете наследовать одну и ту же черту дважды. Вместо этого вы должны использовать какой-то другой механизм. Например, Reverse и Echo являются одновременно манипуляциями с формой волны. У вас может быть

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) 

или что-то подобное.

Если вам требуется больше изменений, чем просто функции, вы должны инкапсулировать изменения в функциональности в своем собственном классе:

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