2017-11-22 28 views
3

私は、次の特性を持っている:奇妙なエラー

trait SomeTrait { 
    def doSomething[V](msg: Message[V]): Message[V] 
} 

メッセージ[V]以下のようにケースクラスです:

case class Message[V](elems: Map[String, V]) 

が、私は今、doSomethingのを呼び出すメソッドを持っているが以下の方法:

val someWork = new SomeTrait { 
    override def doSomething[Int](msg: Message[Int]): Message[Int] = { 
     msg.copy(elems = msg.elems.map { 
     case (k, v) => (k, v + 1) // It fails here!!! 
     }) 
    } 
    } 

私はそれを言って奇妙なコンパイラエラーを取得:

Error:(16, 32) type mismatch; 
found : Int(1) 
required: String 
Error occurred in an application involving default arguments. 
     case (k, v) => (k, v + 1) 

私はどこか愚かでしたか?

+0

注: '+'は、デフォルトの暗黙的に「String」に暗黙的に変換されるため、例外的にエラーメッセージを生成します。あなたが '*'で置き換えると、より賢明なエラーが発生します:value *は、型intのメンバではありません。 – Suma

答えて

3

注意:IntはスカラーではありませんIntあなたはScala Intをジェネリックな型名の後ろに隠しています。あなたが書いたのはC++のテンプレートの特殊化のようですが、ジェネリックはこのようにScalaでは動作しません。あなたのコードで[Int][XXX]に置き換えると、同じエラーが発生します。

メソッドをオーバーライドするときにタイプシグネチャを変更することはできません。特性はどんな制約もなしに任意のタイプのVを受け入れることを約束しているので、実装はこの約束を満たす必要があります。

C++は - 専門のようtype classesを使用して達成することができます - あなたは、必要に応じて、あなたが特別な実装を提供しますと約束して、契約を延長:@Sumaは言っ

case class Message[V](elems: Map[String, V]) 

trait SomeTrait { 
    def doSomething[V: MapValue](msg: Message[V]) 
} 

trait MapValue[V] { 
    def map(v: V): V 
} 

implicit object MapValueInt extends MapValue[Int] { 
    def map(v: Int) = v + 1 
} 

val someWork = new SomeTrait { 
    override def doSomething[X: MapValue](msg: Message[X]) = { 
    val evidence = implicitly[MapValue[X]] 
    msg.elems.map { 
     case (k, v) => k -> evidence.map(v) 
    } 
    } 
} 
1

何...あなたが代わり形質をパラメータ化できこの場合の方法のこれはあまり一般的ではありませんが、少しシンプルです。

trait SomeTrait[V] { 
    def doSomething(msg: Message[V]): Message[V] 
} 

val someWork = new SomeTrait[Int] { 
    def doSomething(msg: Message[Int]) = msg.copy(msg.elems.mapValues(_ + 1) 
} 
+0

違いは、あるケースではパラメータ化されたメソッドを持つ特性を持ち、もう1つのケースでは特性自体がパラメータ化されているため、インスタンスを作成するとそのメソッドのタイプがわかります。何があなたを動かすかわからない。おそらく、あなたは精巧にすることができます... – Dima