10

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) を使用すると、次の利点があります。

  1. 上記のように、関数リテラルを使用して構成できます。
  2. .class ファイルを生成することはありません。(理論的には) コンパイラが行うことは少なくなります。

ただし、次の欠点があります。

  1. 同じ名前空間 (パッケージ) で使用できるようにするには、パッケージ オブジェクトでタイプを定義する必要があります。これは、おそらく使用サイトの別のファイルにあります。
  2. Eclipse のエイリアスで 'Open Type' ctrl-shift-T をジャンプすることはできませんが、Eclipse で Open Declaration (F3) を実行することはできます。これはおそらく将来修正されるでしょう。
  3. Java など、別の言語の型エイリアスを使用することはできません。
  4. 型エイリアスがパラメーター化されている場合、消去により、特性の場合と同じようにパターン マッチングが機能しなくなります。

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

コンパイラは、最初のパターン マッチについて警告を出しますが、これは明らかに期待どおりに動作しません。

では、最後に質問です。特性/クラスを拡張するのではなく、型エイリアスを使用することの利点または欠点は他にありますか?

4

2 に答える 2

8

それらの重要な点は、実際には型エイリアスと特性が本当に異なるということだと思います。違いのリストは延々と続く:

  1. 短縮構文は、型のエイリアス (たとえばx => x+7、 a として機能しますtype I2I = Int => Int) に対して機能し、特性では機能しません。
  2. 特性は追加のデータを運ぶことができますが、型エイリアスはできません。
  3. 暗黙は型エイリアスに対して機能しますが、特性では機能しません。
  4. トレイトは、タイプ エイリアスが提供しない方法でタイプ セーフ/マッチングを提供します。
  5. 型エイリアスには、サブクラスでのオーバーライドに関する厳密な規則があります。同じ名前の特性は代わりにシャドウします (何でも構いません)。

とりわけ。

これは、2 つのケースで劇的に異なることを行っているためです。型エイリアスは、単に「わかりました。Foo と入力するとき、実際には Bar を意味します。それらは同じです。わかりましたか?クールです」と言う方法です。これを行った後、いつでもどこでも好きなときに名前Fooを置き換えることができます。Bar唯一の制限は、タイプが何であるかを決定したら、変更できないことです。

一方、トレイトはまったく新しいインターフェイスを作成します。これは、トレイトが拡張するものを拡張する場合と拡張しない場合があります。そうでない場合でも、これは独自のタイプのエンティティであることを示しており、パターン マッチングや「isInstanceOf」によるテストなどを行うことができます。

それで、それらが本当に異なることを確立したので、問題はそれぞれをどのように使用するかです. 答えは非常に簡単です。既存のクラスが好きで、名前が嫌いな場合は、型エイリアスを使用します。他のものとは異なる独自の新しいエンティティを作成する場合は、特性 (またはサブクラス) を使用します。前者は主に利便性のためであり、後者は型の安全性や機能を追加するためのものです。一方を他方の代わりに使用するという規則は、実際に要点を捉えているとは思いません。両方の機能を理解し、それらが必要な機能である場合にそれぞれを使用してください。

(そして、ジェネリックと同様の機能を提供する存在型があります...しかし、それは別の質問に任せましょう。)

于 2012-10-17T21:36:12.123 に答える
2

それらは、型エイリアスが型等価関係を定義するという点で異なります (つまり、T1 <: T2 && T1 >: T2) が、特性拡張は厳密なサブタイプ関係を定義します (つまり、T1 <: T2 && !(T1 >: T2) )。それらを賢く使用してください。

于 2012-10-18T11:19:41.793 に答える