2010-01-05 24 views

risposta

60

Una classe può solo extend one superclass e, quindi, solo una classe astratta. Se vuoi comporre più classi il modo in Scala è usare mixin class composition: unisci una superclasse (facoltativa), le tue definizioni membro e uno o più traits. Un tratto è limitato rispetto alle classi in quanto non può avere parametri di costruzione (confrontare lo scala reference manual).

Vengono introdotte le restrizioni dei tratti rispetto alle classi per evitare problemi tipici di ereditarietà multipla. Ci sono regole più o meno complicate rispetto alla gerarchia ereditaria; potrebbe essere meglio evitare una gerarchia in cui ciò è veramente importante. ;-) Per quanto ho capito, può avere importanza solo se si ereditano due metodi con la stessa firma/due variabili con lo stesso nome da due diversi tratti.

+0

Non ti imbatti in scenari complicati finché la tua gerarchia è un albero. – Raphael

+0

@edit: puoi avere conflitti senza i supertipi che ereditano lo stesso tratto. Qualsiasi due metodi con lo stesso nome e il parametro (tipo) elencano un conflitto. – Raphael

+0

@Raphael Un buon punto. Ho provato a chiarire questo. È corretto ora? –

5

Almeno in Scala, il sistema di caratteri ha un modo esplicito di dichiarare la priorità padre in una sottoclasse per evitare problemi tipici associati all'ereditarietà multipla, cioè conflitti con metodi ereditati aventi la stessa firma.

I tratti sono simili alle interfacce Java, ma possono avere implementazioni di metodo.

+0

La precedenza del binding non è risolta dai tratti in sé ma dalla linearizzazione della lista dei tipi super. Si potrebbe fare la stessa cosa con l'ereditarietà multipla delle classi. – Raphael

12

Concettualmente, un tratto è un componente di una classe, non una classe di per sé. In quanto tale, in genere non ha costruttori e non è pensato per "stare da solo".

Suggerisco di utilizzare una classe astratta quando ha un significato indipendente e tratti quando si desidera semplicemente aggiungere funzionalità in modo orientato agli oggetti. Se non sei sicuro tra i due, potresti scoprire che se tutti i tuoi metodi ruotano attorno a una singola cosa, probabilmente vuoi un tratto.

Per un esempio (non specifico della lingua), se il Dipendente deve estendere sia "Persona" che "Clonabile", rendi Persona una classe base e Clonabile un tratto.

+1

Direi che quando tutti i metodi ruotano attorno a fare una singola cosa, il tuo design è a posto (Principio di responsabilità singola: http://en.wikipedia.org/wiki/Single_responsibility_principle). Altrimenti il ​​design dovrebbe essere un compromesso difendibile. –

+0

Hai ragione, ho chiarito un po 'la mia risposta. – Oak

49

Un aspetto dei tratti è che sono uno impilabile. Consentire una forma vincolata di AOP (attorno ai consigli).

trait A{ 
    def a = 1 
} 

trait X extends A{ 
    override def a = { 
     println("X") 
     super.a 
    } 
} 


trait Y extends A{ 
    override def a = { 
     println("Y") 
     super.a 
    } 
} 

scala> val xy = new AnyRef with X with Y 
xy: java.lang.Object with X with Y = [email protected] 
scala> xy.a 
Y 
X 
res0: Int = 1 

scala> val yx = new AnyRef with Y with X 
yx: java.lang.Object with Y with X = [email protected] 
scala> yx.a 
X 
Y 
res1: Int = 1 

La risoluzione del super riflette la linearizzazione della gerarchia di ereditarietà.

+2

cool ... cosa succede se due tratti ** X ** e ** Y ** producono valori diversi per def ** a **? –

Problemi correlati