Nel Twitter Effective Scala - Type Aliases, dicono:buon stile: Tipo alias vs sottoclasse vuote classi/tratti
Non utilizzare sottoclassi quando un alias farà.
trait SocketFactory extends (SocketAddress => Socket)
a SocketFactory è una funzione che produce uno zoccolo. Utilizzando un tipo alias
type SocketFactory = SocketAddress => Socket
è meglio. Possiamo ora fornire letterali di funzione per i valori di tipo SocketFactory e anche utilizzare la funzione di composizione: val addrToInet: SocketAddress => Long val inetToSocket: Lungo => Socket
val factory: SocketFactory = addrToInet andThen inetToSocket
notare che la tipologia alias non sono nuovi tipi - che sono equivalenti alla sostituzione sintattica del nome aliasato per il suo tipo.
il genere di cose di cui stiamo parlando è:
trait Base
trait T1 extends Base // subclassing
type T2 = Base // type alias
Ovviamente non si può usare un tipo alias in sostituzione quando la classe/caratteristica ha un corpo o di informazioni negozi.
Quindi utilizzando tipo alias (T2) piuttosto che si estende con un tratto o una classe (T1) presenta i seguenti vantaggi:
- Come si dice sopra, possiamo comporre utilizzando funzioni letterali.
- Non produrremo un file .class, ci sarà meno per il compilatore (teoricamente).
Tuttavia, ha i seguenti svantaggi:
- essere disponibile nello stesso namespace (package), è necessario definire il tipo di un oggetto del pacchetto, che sarà probabilmente in un altro file da il sito di utilizzo.
- Non è possibile saltare "Apri tipo" ctrl-shift-T su un alias in Eclipse, ma è possibile aprire la dichiarazione (F3) in Eclipse. Questo probabilmente verrà risolto in futuro.
- Non è possibile utilizzare l'alias di tipo da un'altra lingua, ad esempio Java.
- Se l'alias di tipo è parametrizzato, la cancellazione impedisce che la corrispondenza del modello funzioni nello stesso modo che per i tratti.
Il quarto punto è il più grave per me:
trait T1[T]
trait T2 extends T1[Any]
type T3 = T1[Any]
class C2 extends T2
val c = new C2
println("" + (c match { case t: T3 => "T3"; case _ => "any" }))
println("" + (c match { case t: T2 => "T2"; case _ => "any" }))
Questo produce:
T3
T2
Il compilatore dà un avvertimento circa il primo pattern match, che chiaramente non funziona come previsto.
Quindi, finalmente, la domanda. Ci sono altri vantaggi o svantaggi nell'usare gli alias di tipo piuttosto che estendere un tratto/classe?
Il pattern match ha senso per me, anche se ho dovuto pensarci per un minuto. 'T3' essendo un alias di tipo significa che puoi sostituire' T1 [Any] 'per' T3' ovunque. Quindi la prima corrispondenza è in realtà 'c match {cast t: T1 [Any] =>" T3 "; ...} '. 'c' è un' C2', che è un sottotipo di 'T1 [Any]' (tramite 'T2'). Quindi * non * corrispondere al modello sarebbe in realtà più sorprendente. – Ben
@ Ben sono d'accordo che funzioni come specificato, quando ci pensi, ma il mio punto era che non è lo stesso di quando hai un tratto. –