0

Gson ライブラリをもう少し Scala フレンドリーにするために、小さなラッパー クラスを作成しようとしています。残念ながら、これを希望どおりに実行しようとすると、コンパイル エラーが発生します。

これは私がこれまでに得たコードです:

package com.test

import com.google.gson.{JsonObject, JsonElement}
import scala.collection.Iterator
import scala.collection.immutable.Map

case class GsonMap ( private val inner: JsonObject = new JsonObject )
    extends Map[String, JsonElement] {

    /** {@inheritDoc} */
    override def iterator: Iterator[(String, JsonElement)]
        = new Iterator[(String, JsonElement)] {
            private val entries = inner.entrySet.iterator
            override def hasNext: Boolean = entries.hasNext
            override def next: (String, JsonElement) = {
                val elem = entries.next
                ( elem.getKey, elem.getValue )
            }
        }

    /**
     * Returns a clone of the inner JsonObject
     */
    private def cloneInner: JsonObject = {
        val result = new JsonObject()
        iterator.foreach { (item) => result.add( item._1, item._2 ) }
        result
    }

    /** {@inheritDoc} */
    override def + ( kv: (String, JsonElement) ): GsonMap = {
        val cloned = cloneInner
        cloned.add( kv._1, kv._2 )
        GsonMap( cloned )
    }

    /** {@inheritDoc} */
    override def get( key: String ): Option[JsonElement]
        = Option( inner.get(key) )

    /** {@inheritDoc} */
    override def - ( key: String ): GsonMap = {
        val cloned = cloneInner
        cloned.remove( key )
        GsonMap( cloned )
    }

}

+これで、メソッドが Map クラスで定義されているものと一致しないことがわかりました。それが問題です、本当に。+メソッドが s を受け入れJsonElementて a を返すようにしたいのGsonMapですが、それを機能させる方法がよくわかりません。この時点でいくつかのバリエーションを試しましたが、うまくいきませんでした

参考までに、これは私が受け取っているコンパイルエラーです:

[info] Compiling 1 Scala source to target/scala-2.9.2/classes...
[error] src/main/scala/GsonMap.scala:7: class GsonMap needs to be abstract, since method + in trait Map of type [B1 >: com.google.gson.JsonElement](kv: (String, B1))scala.collection.immutable.Map[String,B1] is not defined
[error] case class GsonMap ( val inner: JsonObject = new JsonObject )
[error]            ^
[error] src/main/scala/GsonMap.scala:31: method + overrides nothing
[error]     override def + ( kv: (String, JsonElement) ): GsonMap = {
[error]                  ^
[error] two errors found

これについて何かアドバイスはありますか?


アップデート:

以下でも提案されているように、これは私が試したバリエーションの1つです。

override def +[T >: JsonElement] ( kv: (String, T) ): GsonMap = {
    val cloned = cloneInner
    cloned.add( kv._1, kv._2 )
    GsonMap( cloned )
}

ただし、これも失敗します。

[info] Compiling 1 Scala source to target/scala-2.9.2/classes...
[error] /src/main/scala/GSON.scala:33: type mismatch;
[error]  found   : T
[error]  required: com.google.gson.JsonElement
[error]         cloned.add( kv._1, kv._2 )
[error]                               ^
[error] one error found

演算子についての私の理解で>:は、T は JsonElement の親でなければなりませんが、これは私が探しているものではないと思います。この場合、このマップには JsonElements のインスタンスのみを含めることができるため、JsonElements の親を配置することは適切ではありません。

4

3 に答える 3

5

エラーの直接的な原因は、あなたが+JsonElement のみを受け入れるのに対し、+特性の は上限が の型パラメーターを期待していることですJsonElement

override def +[T >: JsonElement] ( kv: (String, T) ): GsonMap = {
    val cloned = cloneInner
    cloned.add( kv._1, kv._2 )
    GsonMap( cloned )
}

その理由は (@Frank の回答で指摘されているように) Map がその値の引数で共変ChildParentあるMap[String,Parent]ためです。Map[String, Child]addMap

scala> class Element;
defined class Element

scala> class SubElement extends Element;
defined class SubElement

scala> val m = Map("foo"-> new SubElement)
m: scala.collection.immutable.Map[java.lang.String,SubElement] = Map(foo -> SubElement@6a63afa4)

scala> m + ("bar" -> new Element)
res0: scala.collection.immutable.Map[java.lang.String,Element] = Map(foo -> SubElement@2e7ff81e, bar -> Element@654ab15b)

scala> m + ("bar" -> new Element) + ("baz" -> "Text")
res1: scala.collection.immutable.Map[java.lang.String,java.lang.Object] = Map(foo -> SubElement@6a63afa4, bar -> Element@233d0d04, baz -> Text)

変更可能なバッキング オブジェクトに不変のトレイトを実装しようとしている場合はMap、この「アップキャスト」を自分で提供する必要があります。または、Scala 標準ライブラリの温かい抱擁に屈して、代わりに を拡張することもできますmutable.Mapまさにあなたのためです。Java 型がインターフェースを実装している場合は、java.util.Map既製のラッパーと暗黙の変換がscala.collection.JavaConversions.

custom で何をしようとしているのかはわかりませんが、拡張する方法がまったくないMap可能性がかなり高いです( Scala コレクション ライブラリの標準イントロでマップを拡張する例では、新しいデータを実装しています構造) であり、ほとんどのコードで Scala マップを処理し、境界で Map を GSON に相当するものに変換するなどの暗黙的なものを提供したい場合。Map

于 2012-10-17T06:32:49.360 に答える
0

エラーはかなり詳細に説明されており、要点は次のとおりです。基本クラスにないものを上書きしようとして、必要なメソッドを実装していません。

解決策に関して、あなたが本質的に見逃したのは、をMap使用する分散アノテーションです。クラスのScalaDocを見ると、次のように表示されます。この小さなことがあなたの問題を引き起こしています。MapMap[A, +B]+

何が起こっているのかを理解するために、共分散を読んでから、+メソッドが異なる型シグネチャを持ち、代わりに、を返す理由を理解することを勧めします。同じことを行う必要があります。これにより、不変オブジェクトのマップを保持できるだけでなく、サブクラスがある場合に共分散から利益を得ることができます。Map[A, B]Map[A, B1]B1 >: BJsonElement

于 2012-10-17T06:34:40.623 に答える
0

「+」メソッドには、次の署名が必要です。+[B1 >: B](kv: (A, B1)): Map[A, B1]

答えというよりも観察です。GSonMap には、JsonObject を受け取り、それを内部で使用するコンストラクターがあります。また、JsonObject を public フィールドとして公開します。問題は、JsonObject が変更可能であり、GsonMap で公開する方法が原因で、後者も変更可能になることです (これは、誰でも JsonObject を外部から変更できるためです)。

そのため、コンストラクターでinnerJsonObject を複製し、内部オブジェクトの代わりに JsonObject の複製コピーを返すメソッドとして公開することを検討してください。このようにして、GsonMap の不変性が保証されます。

于 2012-10-17T09:32:29.383 に答える