2012-10-17 4 views
0

작은 랩퍼 클래스를 작성하여 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) 
    } 

} 

지금, 나는 + 방법은지도 클래스에 정의 된 것과 일치하지 않는 것을 알고

은 내가 지금까지 가지고 코드입니다. 정말 문제입니다. + 메서드가 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 

>: 연산자에 대한 제 이해는 th입니다. T에서 JsonElement의 부모 여야 만합니다. 나는 이것이 내가 찾고있는 것 같지 않습니다. 이 경우이 맵은 JsonElements 인스턴스 만 포함 할 수 있으므로 JsonElements의 부모를 두는 것은 적절하지 않습니다.

답변

5

오류의 직접적인 원인은 +이 JsonElement 만 허용하는 반면 특성의 +은 상한이 JsonElement 인 유형 매개 변수를 필요로한다는 것입니다.

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

이유 (@ 프랭크의 답변을 지적)입니다 ChildParent의 하위 유형이 Map[String,Parent]는, Map[String, Child]의 슈퍼 될 것입니다 경우지도 즉, 값 인수에 공변 점이다이 add 정의 당신이 Map에 "업 추가"할 수 있습니다 : 당신이 변경 가능한 지원 객체의 불변 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 -> [email protected]) 

scala> m + ("bar" -> new Element) 
res0: scala.collection.immutable.Map[java.lang.String,Element] = Map(foo -> [email protected], bar -> [email protected]) 

scala> m + ("bar" -> new Element) + ("baz" -> "Text") 
res1: scala.collection.immutable.Map[java.lang.String,java.lang.Object] = Map(foo -> [email protected], bar -> [email protected], baz -> Text) 

, 또는 당신은의 따뜻한 포옹에 줄 수 있습니다. 스칼라 표준 라이브러리 대신 mutable.Map까지 확장합니다. 이미 precisely that입니다. 자바 유형이 java.util.Map 인터페이스를 구현하는 경우 scala.collection.JavaConversions에 미리 만들어진 래퍼와 암시 적 변환이 있습니다.

Map을 연장 스칼라 수집 라이브러리 구현에 표준 소개에 전혀합니다 (example for extending maps를 이동하는 방법이 아니라고 난 당신이 사용자 정의 Map으로 뭘 하려는지 모르겠지만, 상당히 가능성 새로운 데이터 구조)를 사용하면 대부분의 코드에서 스칼라 맵을 처리하고 예를 들어 경계에서 GSON에 상응하는지도로 변환하십시오.

+0

입력 해 주셔서 감사합니다. 나는 원래 비슷한 것을 시도했지만 운이 없었습니다. 이 질문과 컴파일 오류가 포함되도록 질문을 업데이트했습니다. – Nycto

+0

몇 가지 배경을 추가했습니다. – themel

+0

도움을 주시고 광범위한 답변을 주셔서 감사합니다. 나는 이것이 의심 스럽습니다 – Nycto

0

오류는 꽤 복잡합니다. 기본 클래스에없는 것을 덮어 쓰려고하는데 필요한 메소드를 구현하지 않았습니다.

솔루션의 관점에서 볼 때 본질적으로 놓친 것은 Map이 사용하는 분산 주석입니다. Map 클래스의 ScalaDoc을 보면 다음과 같이 표시됩니다 : Map[A, +B]. 이 작은 +가 문제의 원인입니다. 무슨 일이 일어나고 있는지 이해하기

, 난 당신이 공분산에 읽어 제안하고 + 방법은 다른 유형의 서명을 가지고 있으며, 하지 돌려 Map[A, B] 않지만, 대신 Map[A, B1], B1 >: B 이유는 다음 이해하는 것입니다. 당신은 똑같은 행동을해야합니다. 이것은 불변의 JsonElement 객체의지도를 유지할뿐만 아니라 하위 클래스가있을 때 공분산으로부터 이익을 얻는 것을 허용하기 때문입니다. 답변보다 관찰 +[B1 >: B](kv: (A, B1)): Map[A, B1]

더 :

+0

피드백에 감사드립니다. 이전에 공분산과 반 차분에 대한 책을 읽었지만이 경우에 적용하기 위해 고심하고 있습니다. 내가 다른 코멘트에서 말했듯이, 나는 아직도 어떻게 작동하는지에 대한 나의 정신 모델을 형성하고있다. 나는이 경우에 올바른 방향으로 밀림이 필요하다고 생각한다. – Nycto

0

의 "+"방법은 다음과 같은 서명이 필요 귀하의 GSonMap가 된 JSONObject를 받고 내부적으로 사용하는 생성자가 있습니다. 또한 JsonObject를 공용 필드로 제공합니다. 문제는 JsonObject가 변경 가능하고 GsonMap에 노출하는 방식으로 인해 변경 될 수 있다는 것입니다. 이는 누구나 외부에서 JsonObject를 수정할 수 있기 때문입니다.

생성자에서 JsonObject를 복제하고 inner을 내부 객체 대신 JsonObject 복제 된 복사본을 반환하는 메소드로 표시하는 것을 고려하십시오. 이런 식으로 GsonMap의 불변성이 보장됩니다.

+0

서명이 일치하지 않는다고 생각합니다. 스칼라에서 타입 시스템의 정신 모델을 여전히 고착시키고 있습니다. 내가 시도한 좀 더 구체적인 변이 중 하나에 대해 좀 더 자세하게 질문을 업데이트했다. 내부 변수의 노출에 관해서 : CR에 감사드립니다! 내가 포스팅을 공식화하는 동안 내 역할을 놓쳤다. 아무도이 코드를 복사하지 않도록 질문을 업데이트했으며 (여전히 깨졌지만) 동일한 버그가 있습니다. – Nycto