2016-04-09 8 views
1

Ich habe den Code, instance.get gibt Wert zurück, und basierend auf dem Typ, den ich entsprechend verarbeitet.Merging mehrere Fall (in Übereinstimmung/Fall) in Scala

instance.get match { 
    case v:Range => { 
     val sizeInBytes = util.conversion.Util.getBytesForBits(v.size) 
     val value = v.decode(contentByteArray.slice(index, index + sizeInBytes)) 
     index += sizeInBytes 
     res(key) = value 
    } 
    case v:Encoding => { 
     val sizeInBytes = util.conversion.Util.getBytesForBits(v.size) 
     val value = v.decode(contentByteArray.slice(index, index + sizeInBytes)) 
     index += sizeInBytes 
     res(key) = value 
    } 
    ... 
    } 

Im Code, ich habe Vervielfältigung für den Range und Encoding Typen. Wie kann ich die beiden Fälle zusammenführen?

Ich versuchte den | Operator, aber es funktioniert nicht.

case v:Range | v:Encoding 
+0

Erhalten Sie einen Fehler?Ich habe einige Nachforschungen angestellt, und ich denke, dass diese Art von Code "variable Bindung in alternativen Mustern" von Scala möglicherweise nicht erlaubt wird. Siehe https://issues.scala-lang.org/browse/SUGGEST-25 –

+0

Funktioniert 'case v @ (Bereich | Encoding) => {...}? –

Antwort

5

Dies kann nicht funktionieren, weil Range.size und Encoding.size zwei trotz der Tatsache, völlig unterschiedliche Methoden sind, dass sie das gleiche benannt sind. Und das gleiche gilt für Range.decode und Edncoding.decode. So

, wenn Sie v.size schreiben, hat die Art der v bekannt sein, hat es entweder v:Encoding oder v:Range, nicht v:Encoding|v:Range sein.

Wie behebt man das? Machen Sie ein gemeinsames Merkmal wie folgt aus:

Und dann die Definitionen von Range ändern und Encoding:

class Range extends SomethingWithDecodeAndSize { ... } 
class Encoding extends SomethingWithDecodeAndSize { ... } 

Jetzt einfach case v: SomethingWithDecodeAndSize => ... in Ihrem Spiel-Klausel zu tun.

Auch ... Do not instance.get, das ist schlechter Geschmack. stattdessen Sie

instance match { 
    Some(v: SomethingWithDecodeAndSize) => ... 
} 

aktualisieren Wenn Sie nicht die Definitionen der ursprünglichen Klassen ändern können, können Sie einen Extraktor verwenden:

object SomethingWithDecodeAndSize { 
    def unapply(a: Any): Option[SomethingWithDecodeAndSize] = a match { 
     case r: Range => Some(new SomethingWithDecodeAndSize { 
     def size = r.size 
     def decode(bytes: Array[Byte]) = r.decode(bytes) 
     }) 
     case r: Encoding => Some(new SomethingWithDecodeAndSize { 
     def size = r.size 
     def decode(bytes: Array[Byte]) = r.decode(bytes) 
     }) 
     case _ => None 
    } 
} 

Jetzt können Sie case Some(SomethingWithDecodeAndSize(v)) => ... in Ihrem Spiel tun.

1

Eine alternative Lösung @ Dimas, falls Sie nicht Definition von Range ändern und Encoding (und es gibt keinen übergeordnete Typen mit dem erforderlichen Methoden):

trait RangeOrEncoding { 
    def size: Int 
    def decode(bytes: Array[Byte]): Whatever 
} 

implicit def liftRange(r: Range): RangeOrEncoding = new RangeOrEncoding { 
    def size = r.size 
    def decode(bytes: Array[Byte]) = r.decode(bytes) 
} 

// similar conversion for Encoding 

// can also be a local def 
private def handleRangeOrEncoding(v: RangeOrEncoding) = { 
    val sizeInBytes = util.conversion.Util.getBytesForBits(v.size) 
    val value = v.decode(contentByteArray.slice(index, index + sizeInBytes)) 
    index += sizeInBytes 
    res(key) = value 
} 

instance match { 
    case Some(v: Range) => handleRangeOrEncoding(v) 
    case Some(v: Encoding) => handleRangeOrEncoding(v) 
    ... 
} 
+0

Ich würde in diesem Fall eher einen Extraktor als implizit empfehlen. – Dima

1

ich die Cheerleadern in der High School erinnern, fragen Sie uns, "Wie locker ist deine Gans?"

scala> class C { def f(i: Int) = 2 * i } 
defined class C 

scala> class D { def f(i: Int) = 3 * i } 
defined class D 

scala> def test(x: Any) = x match { case y: { def f(i: Int): Int } => y.f(42) } 
<console>:11: warning: a pattern match on a refinement type is unchecked 
     def test(x: Any) = x match { case y: { def f(i: Int): Int } => y.f(42) } 
              ^
warning: there was one feature warning; re-run with -feature for details 
test: (x: Any)Int 

scala> test(new C) 
res0: Int = 84 

scala> test(new D) 
res1: Int = 126 

scala> test(42) 
java.lang.NoSuchMethodException: java.lang.Integer.f(int) 
    at java.lang.Class.getMethod(Class.java:1786) 
    at .reflMethod$Method1(<console>:11) 
    at .test(<console>:11) 
    ... 32 elided 

Ich glaube, die Antwort war: "Lose, Baby, lose."

Edit:

scala> import reflect.runtime._,universe._,language.reflectiveCalls 
import reflect.runtime._ 
import universe._ 
import language.reflectiveCalls 

scala> class C { def f(i: Int) = 2 * i } 
defined class C 

scala> class D { def f(i: Int) = 3 * i } 
defined class D 

scala> def f[A](a: A)(implicit tt: TypeTag[A]) = a match { 
    | case b: { def f(i: Int): Int } 
    | if tt.tpe <:< typeOf[{ def f(i: Int): Int }] => 
    | b.f(42) 
    | } 
<console>:19: warning: a pattern match on a refinement type is unchecked 
     case b: { def f(i: Int): Int } 
      ^
f: [A](a: A)(implicit tt: reflect.runtime.universe.TypeTag[A])Int 

scala> f(new C) 
res0: Int = 84 

scala> f(new D) 
res1: Int = 126 

scala> f(3) // now an ordinary MatchError 
scala.MatchError: 3 (of class java.lang.Integer) 
    at .f(<console>:18) 
    ... 32 elided 

So können Sie es als eine gewöhnliche Typ Grenzen ausdrücken:

scala> def f[A <: { def f(i: Int): Int }](a: A) = a.f(42) 
f: [A <: AnyRef{def f(i: Int): Int}](a: A)Int 

scala> f(new C) 
res3: Int = 84 

scala> f(17) 
<console>:20: error: inferred type arguments [Int] do not conform to method f's type parameter bounds [A <: AnyRef{def f(i: Int): Int}] 
     f(17) 
    ^
<console>:20: error: type mismatch; 
found : Int(17) 
required: A 
     f(17) 
     ^

Sie müssen noch die Kosten des reflektierenden Anrufs anzunehmen, natürlich.

+0

Was ist mit der Verwendung von Typ-Tags und '=: ='? – badcook

+0

Ignorieren Sie die Warnungen nicht. Sie sind wichtig und sinnvoll. Und erklären Sie genau, warum Sie nicht tun sollten, was Sie hier tun. – Dima

+1

@ badcook Etwas wie. –