ScalaのTraitsは「ダイヤモンドエラー」をどのように回避しますか?


16

(注:明白な理由のためにタイトルで「問題」の代わりに「エラー」を使用しました。;))。

ScalaでTraitsの基本的な読書をしました。これらはJavaまたはC#のインターフェイスに似ていますが、メソッドのデフォルト実装を許可します。

私は不思議に思っていました:これは「ダイヤモンドの問題」のケースを引き起こすことはできません。それが多くの言語がそもそも多重継承を避ける理由です。

その場合、Scalaはこれをどのように処理しますか?


あなたの研究を共有することは皆を助けます。試したことと、それがニーズに合わなかった理由を教えてください。これは、あなたが時間をかけて自分自身を助けようとしていることを示しており、明白な答えを繰り返すことから私たちを救い、何よりも、より具体的で関連性のある答えを得るのに役立ちます。参照してください掲載する方法
GNAT

2
@gnat:これは概念的な質問であり、具体的な問題の質問ではありません。「Scalaにこのクラスがあり、ダイヤモンドの問題に関連すると思われる問題が発生している場合、どうすれば修正できますか?」コメントは適切ですが、質問はSOに属します。:P
メイソンウィーラー14

@MasonWheeler私もScalaで基本的な読書をしました。そして、私が読んだもので最初に「ダイヤモンド」を検索すると、答えが得られました:「トレイトはJavaインターフェース構成のすべての機能を備えています。しかし、トレイトはメソッドを実装できます。多くの特性を単一のクラスに混在させることができます。特性はコンストラクタパラメータを取ることはできませんが、クラスのように動作すること以外は、ダイヤモンドの問題なしに多重継承に近づくことができます。この問題の努力の欠如はかなり露骨な感じ
GNAT

7
そのステートメントを読んでも、それがどのように行われたかを教えてくれません。
マイケルブラウン14

回答:


21

ダイヤモンドの問題は、選択するメソッドの実装を決定できないことです。Scalaは、言語仕様の一部として選択する実装を定義することでこれを解決します(このウィキペディアの記事でScalaに関する部分を読んでください)。

もちろん、同じ順序定義をクラスの多重継承で使用することもできますが、なぜ特性に煩わされるのでしょうか?

IMOがコンストラクターである理由。コンストラクターには、通常のメソッドにはないいくつかの制限があります-それらはオブジェクトごとに1回しか呼び出すことができず、新しいオブジェクトごとに呼び出す必要があり、子クラスのコンストラクターは最初の命令として親のコンストラクターを呼び出す必要があります(ほとんどの言語はパラメータを渡す必要がない場合は、暗黙的に行います)。

BとCがAとDを継承し、BとCの両方のコンストラクターがAのコンストラクターを呼び出す場合、DのコンストラクターはAのコンストラクターを2回呼び出します。Scalaがメソッドで行ったように選択する実装を定義することは BとCの両方のコンストラクターを呼び出す必要があるためここでは機能しません。

トレイトにはコンストラクターがないため、この問題を回避できます。


1
C3線形化を使用して、コンストラクターを1回だけ呼び出すことができます。これが、Pythonが多重継承を行う方法です。さらに、D <B | C <Aダイアモンドの線形化はD-> B-> C-> Aです。さらに、Google検索でScalaの特性は可変変数を持つことができることがわかったので、コンストラクタはどこかにありますか?しかし、それはフード(私は知らない、スカラ座を使用したことがない)、それはBとCはAのインスタンスで共有できることを確認するために難しいことではありません...下組成物を用いていた場合
Doval

...トレイトは、インターフェイスの継承と構成+委任を組み合わせたすべての定型文を表現する非常に簡潔な方法のように見えます。これは、動作を再利用する正しい方法です。
ドーバル

@Doval多重継承Pythonでのコンストラクターの私の経験は、コンストラクターが非常に痛いということです。各コンストラクターは、呼び出される順序を知ることができないため、親コンストラクターのシグネチャがわからない。通常の解決策は、すべてのコンストラクターが一連のキーワード引数を取り、未使用の引数をそのスーパーコンストラクターに渡すことですが、その規則に従わない既存のクラスで作業する必要がある場合、安全に継承することはできませんそれ。
James_pic

別の質問は、なぜC ++がダイアモンドの問題に対して正気なポリシーを選択しなかったのかということです。
ユーザー

20

Scalaは、「特性線形化」と呼ばれるものによってダイヤモンドの問題を回避します。基本的に、右から左に拡張する特性でメソッド実装を検索します。簡単な例:

trait Base {
   def op: String
}

trait Foo extends Base {
   override def op = "foo"
}

trait Bar extends Base {
   override def op = "bar"
}

class A extends Foo with Bar
class B extends Bar with Foo

(new A).op
// res0: String = bar

(new B).op
// res1: String = foo

そうは言っても、ルックアップする特性のリストには、他の特性を拡張する可能性があるため、明示的に指定したものよりも多く含まれている場合があります。詳細な説明はここにあります: スタック可能な変更としての特性 と、ここにある線形化のより完全な例: なぜ多重継承しないのですか?

他のプログラミング言語では、この動作は「メソッド解決順序」または「MRO」と呼ばれることがあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.