回答:
2つの違いが考えられます
Scalaでのプログラミングには、「特性を評価するか、評価しないか」というセクションがあります。この質問に対処します。第1版はオンラインで入手できるので、ここですべてを引用してもかまいません。(Scalaの真面目なプログラマは本を買うべきです):
再利用可能な動作のコレクションを実装するときは常に、特性または抽象クラスのどちらを使用するかを決定する必要があります。明確な規則はありませんが、このセクションには、考慮すべきいくつかのガイドラインが含まれています。
動作が再利用されない場合は、それを具象クラスにします。結局それは再利用可能な振る舞いではありません。
複数の無関係なクラスで再利用される可能性がある場合は、それをトレイトにします。クラス階層のさまざまな部分に混在できるのは、特性のみです。
Javaコードで継承する場合は、抽象クラスを使用します。コード付きのトレイトにはJavaの類似物がないため、Javaクラスのトレイトから継承するのは面倒です。一方、Scalaクラスからの継承は、Javaクラスからの継承とまったく同じです。1つの例外として、抽象メンバーのみのScalaトレイトはJavaインターフェースに直接変換されるため、Javaコードが継承することを期待している場合でも、そのようなトレイトを自由に定義してください。JavaとScalaの併用の詳細については、第29章を参照してください。
コンパイルされた形式で配布する予定で、外部のグループがそれを継承するクラスを作成することを期待している場合は、抽象クラスを使用する傾向があります。問題は、トレイトがメンバーを取得または失ったとき、継承されたクラスは、変更されていなくても再コンパイルする必要があることです。外部のクライアントがビヘイビアから継承するのではなく、ビヘイビアを呼び出すだけの場合は、トレイトを使用することで問題ありません。
効率が非常に重要な場合は、クラスの使用に傾けます。ほとんどのJavaランタイムは、クラスメンバーの仮想メソッド呼び出しを、インターフェイスメソッド呼び出しよりも高速な操作にします。トレイトはインターフェースにコンパイルされるため、わずかなパフォーマンスオーバーヘッドが発生する可能性があります。ただし、この選択は、問題の特性がパフォーマンスのボトルネックを構成していることがわかっており、代わりにクラスを使用すると実際に問題が解決するという証拠がある場合にのみ選択してください。
それでもわからない場合は、上記を検討した後、まずは特性として作成してください。後でいつでも変更できます。通常、トレイトを使用すると、より多くのオプションが開いたままになります。
@Mushtaq Ahmedが述べたように、トレイトはクラスのプライマリコンストラクターに渡されるパラメーターを持つことができません。
もう1つの違いは、の扱いですsuper
。
クラスと特性のもう1つの違いは、クラスで
super
は呼び出しが静的にバインドされるのに対し、特性では呼び出しが動的にバインドされることです。super.toString
クラスを記述する場合、どのメソッド実装が呼び出されるかが正確にわかります。ただし、同じことを特性で記述した場合、特性を定義するときに、スーパー呼び出しのために呼び出すメソッド実装は未定義です。
詳細については、第12章の残りの部分を参照してください。
編集1(2013):
抽象クラスの動作は、特性と比較して微妙に異なります。線形化ルールの1つは、クラスの継承階層を保持することです。これにより、抽象クラスをチェーンの後半にプッシュする傾向がありますが、特性をうまく組み合わせることができます。特定の状況では、クラス線形化の後の位置にあることが実際に望ましいですなので、そのために抽象クラスを使用できます。Scalaのクラスの線形化の制約(混合順)を参照してください。
編集2(2018):
Scala 2.12以降、トレイトのバイナリ互換動作が変更されました。2.12より前のバージョンでは、クラスを変更していなくても、メンバーを特性に追加または削除するには、特性を継承するすべてのクラスの再コンパイルが必要でした。これは、JVMでの特性のエンコード方法が原因です。
Scala 2.12以降、トレイトはJavaインターフェイスにコンパイルされるため、要件は少し緩和されました。トレイトが次のいずれかを行う場合でも、そのサブクラスは再コンパイルが必要です。
- フィールドの定義(
val
またはvar
、ただし定数は問題ありfinal val
ません- 結果タイプなし)- 呼び出す
super
- 本文の初期化ステートメント
- クラスを拡張する
- 線形化に依存して正しいスーパートレイトの実装を見つける
しかし、トレイトがそうでない場合は、バイナリ互換性を損なうことなくそれを更新できます。
If outside clients will only call into the behavior, instead of inheriting from it, then using a trait is fine
-ここで違いは何ですか?extends
対with
?
extends
とはwith
。それは純粋に構文です。複数のテンプレートから継承する場合、最初のextend
get with
、他のすべてのget 、それだけです。with
コンマと考えてください:class Foo extends Bar, Baz, Qux
。
複数の抽象クラスを直接拡張することはできませんが、複数の特性を1つのクラスに混在させることはできますが、特性のスーパー呼び出しは動的にバインドされるため、特性はスタック可能であることを言及する価値があります(以前に混合されたクラスまたは特性を参照しています)現在のもの)。
抽象クラスと特性の違いにおけるトーマスの答えから:
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 = $anon$1@6e9b6a
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 = $anon$1@188c838
scala> yx.a
X
Y
res1: Int = 1
でプログラミングScalaの著者は、抽象クラスは、古典的なオブジェクト指向「は、」関係の特性は、組成物のスカラ座通行している間にすることを言います。
抽象クラスは振る舞いを含むことができます-それらはコンストラクターの引数(特性はできません)でパラメーター化でき、作業エンティティーを表します。代わりに、特性は単一の機能、つまり1つの機能のインターフェースを表します。
trait Enumerable
多くのヘルパー関数で定義する場合、それらを動作と呼ぶのではなく、1つの機能に関連付けられた機能のみと呼びます。