key
の構造タイプは定義できますが、union
の構造タイプは定義できません。構造型は、それ自身の外側で定義された抽象型を参照しないことがあります。だから、これは動作しません。
trait Tree[T <: { def union(x: T): T }]
あなたはしかし、Tree
の要素を利用できるようにしなければならない特性を定義することがあります。
trait TreeVal[T] {
type A
def key: A
def union(x: T): T
}
これは2つの方法を使用することができます。最初に、クラスはそのインターフェイスを実装する必要があります。これは、どのクラスをキーとして使用できるかに重大な制約を課します。これは次のようになります:
trait Tree[T <: TreeVal[T]]
また、このように、暗黙の型変換として提供することができます。これは、バインドビューと呼ばれるものを使用
class IntVal(v: Int) extends TreeVal[Int] {
type A = Int
def key: A = v
def union(x: Int): Int = x + v
}
implicit def IntIsVal(v: Int): IntVal = new IntVal(v)
class Tree[T <% TreeVal[T]] // must be class, so it can receive parameters
。詳細を参照してください。しかし暗黙的な変換が定義されており、範囲がTreeVal
であるものを扱うことができると言えば十分です。また
class Tree[T <% TreeVal[T]](node: T, left: Option[Tree[T]], right: Option[Tree[T]]) {
override def toString = "(%s < %s > %s)" format (left.getOrElse("o"), node.key, right.getOrElse("o"))
}
、あなたはいくつかの変更で、型クラスのパターンとそれを使用することができます:たとえば
trait TreeVal[T] {
type A
def key(v: T): A
def union(x: T, y: T): T
}
class Tree[T : TreeVal] // must be class, so it can receive parameters
型クラスのパターンは、コンテキスト境界を使用しています。詳細はこちらをご覧ください。このスタイルは、以前のスタイルよりも一般的に好まれています。なぜなら、これは多くの点でより柔軟性があるからです。それにもかかわらず、どちらも機能します。この場合
、一つはこのようにそれを使用します。
implicit object IntVal extends TreeVal[Int] {
type A = Int
def key(v: Int) = v
def union(x: Int, y: Int) = x + y
}
class Tree[T: TreeVal](node: T, left: Option[Tree[T]], right: Option[Tree[T]]) {
val treeVal = implicitly[TreeVal[T]]
import treeVal._
override def toString = "(%s < %s > %s)" format (left.getOrElse("o"), key(node), right.getOrElse("o"))
}
優秀な説明、ありがとう! – newf
しかし、もう1つの質問:なぜ暗黙のオブジェクトIntVal'に '型A'を明示的に指定すべきですか? 'def key'定義から推定できますか? – newf
おそらく、 'key'メソッドのパラメータ化されたバージョンを使うことができます:def key [A]:A' – incrop