2016-10-13 1 views
3

私は形質を拡張するときに、使用するメソッドの実装を選択できます。ここのように:私はそれがだ、自己のタイプと同じことを行いたい場合は、それが不可能なようスカラ。自己型でスーパーメソッドの実装を選択する方法はありますか?

object Main { 

    def main(args: Array[String]): Unit = { 
    val c = new C 
    println(c.a) 
    println(c.b) 
    } 

    trait Parent { 
    def foo: String 
    } 

    trait A extends Parent { 
    override def foo = "from A" 
    } 

    trait B extends Parent { 
    override def foo = "from B" 
    } 

    class C extends A with B { 
    val b = super[A].foo 
    val a = super[B].foo 
    } 

} 

しかしそうです:これはコンパイルされません

object Main { 

    def main(args: Array[String]): Unit = { 
    val c = new C with A with B 
    println(c.a) 
    println(c.b) 
    } 

    trait Parent { 
    def foo: String 
    } 

    trait A extends Parent { 
    override def foo = "from A" 
    } 

    trait B extends Parent { 
    override def foo = "from B" 
    } 

    class C { 
    self: A with B => 

    val b = super[A].foo 
    val a = super[B].foo 
    } 

} 

。私は正しいとそれは不可能ですか?もし私が正しいのであれば、それはなぜですか、それに対して回避策がありますか?

更新日: なぜ私は最初の場所に必要ですか?私はコンストラクタインジェクションの代わりにセルフタイプを使って依存性インジェクションをして遊んでいました。だから私はベースの特性を持っていたコンバータと子の特性FooConverterとBarConverter。そして、私は(もちろん動作しません)そのようにそれを書きたい:

object Main { 

    class Foo 

    class Bar 

    trait Converter[A] { 
    def convert(a: A): String 
    } 

    trait FooConverter extends Converter[Foo] { 
    override def convert(a: Foo): String = ??? 
    } 

    trait BarConverter extends Converter[Bar] { 
    override def convert(a: Bar): String = ??? 
    } 

    class Service { 

    this: Converter[Foo] with Converter[Bar] => 

    def fooBar(f: Foo, b:Bar) = { 
     convert(f) 
     convert(b) 
    } 
    } 

} 

私はそれがためにジェネリック医薬品のだと思ったが、それはそうではないということになりました。だから、どういうわけか、自分のタイプの選択された形質のスーパーメソッドを呼び出すことが可能かどうかは疑問だった。単純な継承が可能なためです。私の元の問題については、私はこのようにそれを書くことができ、それが動作します:

object Main { 

    class Foo 

    class Bar 

    trait Converter[A] { 
    def convert(a: A): String 
    } 

    trait FooConverter extends Converter[Foo] { 
    override def convert(a: Foo): String = ??? 
    } 

    trait BarConverter extends Converter[Bar] { 
    override def convert(a: Bar): String = ??? 
    } 

    class Service { 

    this: FooConverter with BarConverter => 

    def fooBar(f: Foo, b:Bar) = { 
     convert(f) 
     convert(b) 
    } 
    } 

} 

おそらくタイトな抽象化を、私はそれがこのような状況のために悪いかはわからないと私はコンバータのような幅広い抽象化が必要な場合[A]まったく。

+0

:あなたはAppオブジェクト(あなたの例ではfooはまだval c = new C with A with Bからアクセス可能である)からfooConverter/barConverterにアクセスすることができないよう

このカプセル化は、自己型ベースのものよりさらに強力です。それは不可能。そして、回避策があるとは思わない(特質に "foos"を追加する以外にも:def AFoo = "from A"; override def foo = aFoo' – Dima

+0

なぜあなたはこれを望む? –

+0

@mz質問 –

答えて

1

は(あなただけの内側からそれを行うことができます)は不可能です。あなたの例では、fooを実行時に構築されるインスタンスselfに呼び出そうとしているので、fooは仮想であり、上書きすることができます。コンパイラは実際の実装が呼び出されるかどうかを知りません)。技術的には、あなたが望むものを行うことは不可能です(仮想メソッドを静的メソッドと呼ぶ)。

ナイーブハックは以下のとおりです。

trait CC extends A with B { 
    val b = super[A].foo 
    val a = super[B].foo 
} 

class C { 
    self: CC => 

} 

それは基本的にあなたがしたいのカプセル化を提供します - 彼らはあなたまで(タイプC自体で)利用できるようにするつもりはないとして、あなたはクラスCにabを再定義したいかもしれませんCCCと混合する。すべての例では、あなたが(私の素朴な解決策を含む)を提供することを

注 - 結果val cはとにかくfooへのアクセス権を持ち、どのfooを強要が呼ばれるように起こっているあなたはABA with BまたはB with A)を混ぜない方法によって異なります。したがって、あなたが得る唯一のカプセル化はタイプCそれ自体がfooメソッドを持っていないことです。つまり、self-typeは、LSPに違反することなく、「サブクラス」内のメソッドを一時的に閉じる(プライベートにする)方法を提供しますが、それだけではありません(下記参照)。そのすべてに加えて


、あなたが実装しようとしているケーキ噴射は、いくつかのauthorsで非現実的と考えられています。 Thin Cake Patternを見てみるといいかもしれません - 実際のプロジェクトで(コンストラクタインジェクションと組み合わせて)このようなものをうまく使っています。

私はこの方法であなたのコンバータサービスを実装します:

class Foo 

class Bar 

trait Converter[A] { 
    def convert(a: A): String 
} 

object FooConverter1 extends Converter[Foo] { 
    override def convert(a: Foo): String = ??? 
} 

object BarConverter1 extends Converter[Bar] { 
    override def convert(a: Bar): String = ??? 
} 


trait FooBarConvertService { 
    def fooConverter: Converter[Foo] 
    def barConverter: Converter[Bar] 

    def fooBar(f: Foo, b: Bar) = { 
    fooConverter(f) 
    barConverter(b) 

    } 

} 

trait Converters { 
    def fooConverter: Converter[Foo] = FooConverter1 
    def barConverter: Converter[Bar] = BarConverter1 
} 

object App extends FooBarConvertService with Converters with ... 

これは、すべて一緒にそれを置くとき、あなたは/モックコンバータの実装を変更することができます。

私はまたConverter[Bar]は何もなく、Function1[Bar, String]またはちょうどBar => Stringではありませんので、実際にはそのための別のインタフェースを必要としないことに気づくだろう:

sealed trait FooBar //introduced it just to make types stronger, you can omit it if you prefer 
class Foo extends FooBar 
class Bar extends FooBar 

trait FooBarConvertService { 

    type Converter[T <: FooBar] = T => String 

    def fooConverter: Converter[Foo] 
    def barConverter: Converter[Bar] 
    def fooBar(f: Foo, b: Bar) = { 
    fooConverter(f) 
    barConverter(b) 
    } 

} 

trait FooConverterProvider { 
    def fooConverter: Foo => String = ??? 
} 

trait BarConverterProvider { 
    def barConverter: Bar => String = ??? 
} 

object App 
    extends FooBarConvertService 
    with FooConverterProvider 
    with BarConverterProvider 

あなたはまた、代わりにdef fooConverter: Foo => String = ???def fooConverter(f: Foo): String = ???を使用することができます。

カプセル化について話をすると、推移的な依存関係にアクセスできるようになるにつれて弱くなります。本当に必要な場合は、private[package]修飾子を使用してください。

コンバータモジュール:

package converters 

trait FooBarConvertService { 

    type Converter[T <: FooBar] = T => String 

    private[converters] def fooConverter: Converter[Foo] 
    private[converters] def barConverter: Converter[Bar] 

    def fooBar(f: Foo, b: Bar) = { 
    fooConverter(f) 
    barConverter(b) 
    } 

} 

trait FooConverterProvider { 
    private[converters] def fooConverter: Foo => String = ??? 
} 

trait BarConverterProvider { 
    private[converters] def barConverter: Bar => String = ??? 
} 

コアモジュール:

package client 
import converters._ 

object App 
    extends FooBarConvertService 
    with FooConverterProvider 
    with BarConverterProvider 

希望する場合は、オブジェクトobject converters {...}; object client {...}代わりのパッケージを使用することができます。あなたが右

client.App.fooBar(new Foo, new Bar) //OK 
client.App.fooConverter 
<console>:13: error: method fooConverter in trait FooConverterProvider cannot be accessed in object client.App 
      client.App.fooConverter 
        ^
+1

ありがとう、それはまさに私が探していたものです。 –

1

自己タイプは、混合する特性を使用するクライアントコードが別の特性に混在する必要があることを要求できるようにすることを覚えておいてください。言い換えれば、依存関係を宣言する方法です。しかし古典的な継承ではありません。だからクラスC {self:A with B =>}と言うとき、AとBは実際にはそこにはありません。あなたは、クライアントコードをAとBで混ぜてCで混ぜ合わせなければならないと定義しました。

しかし、このコードのような目的で同じ目標を達成できるようです。言い換えれば、まず第三の形質を作り、それを特定のクラスに拡張する。すでに構築タイプからsuperメソッドを呼び出す

object DoubleSelfType extends App { 
     val c = new DoubleFoo 
     println(c.a) 
     println(c.b) 

     trait Parent { 
     def foo: String 
     } 

     trait A extends Parent { 
     override def foo = "from A" 
     } 

     trait B extends Parent { 
     override def foo = "from B" 
     } 

     trait C { 
     self: A with B => 

     val a = "" 
     val b = "" 

     } 

     class DoubleFoo extends C with A with B { 
     override val b = super[A].foo 
     override val a = super[B].foo 
     } 

    } 
+0

特色の値をオーバーライドしないほうが良いhttp://scalapuzzlers.com/#pzzlr-005 – iuriisusuk

+0

あなたの答えをありがとうございます。素晴らしいですが、私が望んでいたものではありません。タイプDoubleFooにAとBの形質からパブリックメソッド私は何をしようとしたことに興味がある場合に私の質問を更新しました –

関連する問題