2013-01-05 13 views
6

Ho una classe di base astratta (Base) che ha alcuni tratti di stack definiti per esso (StackingTrait).Scala: mixin trait con classe base astratta

trait Base { 
    def foo 
} 
trait StackingTrait extends Base { 
    abstract override def foo { super.foo } 
} 

Sarebbe molto conveniente per implementare una sottoclasse utilizzando la seguente sintassi, ma questo non funziona perché il compilatore dice che foo deve essere dichiarato con override e poi con abstract override sulla ricompilazione, che non è valido perché Impl è una classe.

class Impl extends Base with StackingTrait { 
    def foo {} 
} 

Non riesco a pensare a una buona ragione per cui tale sintassi non sia consentita; foo è definito logicamente con in modo che l'ordine di sovrapposizione si verifichi concettualmente nello stesso modo.

Nota: Ho trovato questa soluzione alternativa che farà effettivamente la stessa cosa che voglio, ma la necessità di una classe di supporto mi fa desiderare una soluzione migliore.

class ImplHelper extends Base { 
    def foo {} 
} 
class Impl extends ImplHelper with StackingTrait 

Perché la sintassi desiderata non viene compilata ed esiste una soluzione elegante?

risposta

4

La mia comprensione è che mentre il messaggio di errore può essere confuso, il comportamento è corretto. foo viene dichiarata come abstract override in StackingTrait, e quindi, in ogni classe concreta che mescola StackingTrait ci deve essere un calcestruzzo (non contrassegnati come abstract) attuazione fooprimaStackingTrait (rispetto all'ordine di linearizzazione). Questo perché super riferisce al tratto poco prima nell'ordine linearizzazione, quindi non deve assolutamente essere una concreta attuazione foo prima StackingTrait viene miscelato nella, o super.foo avrebbe senso.

Quando si esegue questa operazione:

class Impl extends Base with StackingTrait { 
    def foo {} 
} 

l'ordine di linearizzazione è Base <-StackingTrait <-Impl. L'unica caratteristica prima di StackingTrait è Base e Base non definisce un'implementazione concreta di foo.

Ma quando si esegue questa operazione:

traitImplHelper extends Base { 
    def foo {} 
} 
class Impl extends ImplHelper with StackingTrait 

L'ordine di linearizzazione diventa: Base <-ImplHelper <-StackingTrait <-Impl Qui ImplHelper contiene una definizione concreta di foo, ed è sicuramente primaStackingTrait.

Per quello che vale, se avessi mixato ImplHelper dopo StackingTrait (come in class Impl extends StackingTrait with ImplHelper) avresti ancora lo stesso problema e non riuscirebbe a compilare.

Quindi, questo aspetto è abbastanza coerente con me. Non sono a conoscenza di un modo per farlo compilare come previsto.Tuttavia, se sei più preoccupato di semplificare la scrittura di Impl (e di poter definire foo proprio lì senza bisogno di una classe/tratto distinto) piuttosto che semplificare la scrittura di Base o StackingTrait, puoi comunque farlo:

Proprio come nella versione originale si forza ogni classe di calcestruzzo a implementare foo (nel formato fooImpl) e questa volta viene compilato. Il lato negativo è che mentre fooImpl non deve chiamare super.foo (non ha senso e andrà in un ciclo infinito), il compilatore non ti avviserà.

+0

Ottima risposta! Stavo per pubblicare una domanda su questo stesso scenario. Grazie! –

+0

Ho già riscontrato questo problema. La linearizzazione alla fine ha ragione su questo, ma ho capito che lo scopo reale del raggruppamento dei caratteri è questo: puoi apportare una modifica con una caratteristica di impilamento solo a un'implementazione concreta esistente della comune classe/tratto astratti. Vale a dire. avere un 'abstract class List' e un' Trait Mod extends List'. Non puoi semplicemente scrivere 'classe Foo estende List con Mod'. Innanzitutto, devi avere una lista concreta, come 'classe LinkedList estende List', quindi puoi scrivere' class Bar estende LinkedList con Mod'. Spero che abbia un senso. –

+0

Ho dimenticato di aggiungere che questo riguarda solo i tratti con metodi di "override astratto", per essere chiari. Senza tali metodi sembra che i tratti possano essere mescolati liberamente. –