我正在尝试编写一个小包装类,以使Gson library更加友好。不幸的是,当我试图按照我想要的方式进行时,我遇到了编译错误。
这是我到目前为止的代码:
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类中定义的方法不匹配。真的,这就是问题所在。我希望+
方法接受JsonElement
并返回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
有关于此的任何建议吗?
更新
正如下面的建议,这是我尝试过的变种之一:
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的父项是不合适的。
答案 0 :(得分:5)
错误的直接原因是您的+
只接受JsonElement,而特征中的+
需要一个上限为JsonElement
的类型参数。
override def +[T >: JsonElement] ( kv: (String, T) ): GsonMap = {
val cloned = cloneInner
cloned.add( kv._1, kv._2 )
GsonMap( cloned )
}
原因是(正如@Frank的答案所指出的),Map在其值参数中是协变的,即如果Child
是Parent
的子类型,Map[String,Parent]
将是Map[String, Child]
的超类型,此add
定义允许您“向上添加”Map
:
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
,它已经为您precisely that。如果您的Java类型实现了java.util.Map
接口,那么scala.collection.JavaConversions
中甚至还有现成的包装器和隐式转换。
我不知道您要对自定义Map
尝试做什么,但很可能延长Map
根本不可能({3}在Scala集合库的标准介绍中实现了一个新的数据结构),你宁愿在大多数代码中处理Scala映射,然后提供隐含的例如将Map转换为边界处的GSON等效值。
答案 1 :(得分:0)
错误非常详细,并且重点:您尝试覆盖不在基类中的某些内容,并且您没有实现所需的方法。
就解决方案而言,您基本上错过的是Map
使用的方差注释。查看Map
课程的ScalaDoc,您会看到:Map[A, +B]
。这个小+
导致了你的问题。
要了解正在发生的事情,我建议您阅读协方差,然后了解为什么+
方法具有不同的类型签名并且不返回{{1而是Map[A, B]
,而Map[A, B1]
。您应该这样做,因为这样您不仅可以保留不变B1 >: B
个对象的地图,还可以在拥有子类时从协方差中获益。
答案 2 :(得分:0)
“+”方法需要具有以下签名:+[B1 >: B](kv: (A, B1)): Map[A, B1]
更多的是观察而不是答案:您的GSonMap有一个构造函数,它接收一个JsonObject并在内部使用它。它还将JsonObject公开为公共字段。问题是JsonObject是可变的,并且由于你在GsonMap中暴露它的方式,后者也变得可变(因为任何人都可以从外部修改JsonObject)。
因此,请考虑在构造函数中克隆JsonObject并公开inner
作为返回JsonObject的克隆副本而不是内部对象的方法。通过这种方式,可以保证GsonMap的不变性。