TwitterのEffective Scala - Type Aliasesでは、次のように述べています。
エイリアスで十分な場合は、サブクラス化を使用しないでください。
trait SocketFactory extends (SocketAddress => Socket)
SocketFactory は、Socket を生成する関数です。型エイリアスの使用
type SocketFactory = SocketAddress => Socket
優れている。SocketFactory 型の値に関数リテラルを提供し、関数合成も使用できるようになりました。 val addrToInet: SocketAddress => Long val inetToSocket: Long => Socket
val factory: SocketFactory = addrToInet andThen inetToSocket
型エイリアスは新しい型ではないことに注意してください — それらは、その型をエイリアス化された名前に構文的に置き換えることと同等です。
私たちが話していることの種類は次のとおりです。
trait Base
trait T1 extends Base // subclassing
type T2 = Base // type alias
クラス/特性に本体があるか、情報が格納されている場合、明らかに型エイリアスを代わりに使用することはできません。
したがって、トレイトまたはクラス (T1) で拡張するのではなく、型エイリアス (T2) を使用すると、次の利点があります。
- 上記のように、関数リテラルを使用して構成できます。
- .class ファイルを生成することはありません。(理論的には) コンパイラが行うことは少なくなります。
ただし、次の欠点があります。
- 同じ名前空間 (パッケージ) で使用できるようにするには、パッケージ オブジェクトでタイプを定義する必要があります。これは、おそらく使用サイトの別のファイルにあります。
- Eclipse のエイリアスで 'Open Type' ctrl-shift-T をジャンプすることはできませんが、Eclipse で Open Declaration (F3) を実行することはできます。これはおそらく将来修正されるでしょう。
- Java など、別の言語の型エイリアスを使用することはできません。
- 型エイリアスがパラメーター化されている場合、消去により、特性の場合と同じようにパターン マッチングが機能しなくなります。
4番目のポイントは、私にとって最も深刻です。
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" }))
これにより、次が生成されます。
T3
T2
コンパイラは、最初のパターン マッチについて警告を出しますが、これは明らかに期待どおりに動作しません。
では、最後に質問です。特性/クラスを拡張するのではなく、型エイリアスを使用することの利点または欠点は他にありますか?