Scala

2016-04-09 22 views
1

'da birden çok örneği (eşleşme/durum) birleştirme instance.get'un değeri döndürdüğüme ve buna göre I türüne göre işleme koyduğum bir kod var. kodunda 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 
    } 
    ... 
    } 

, ben Range ve Encoding türü için tekrarını var. İki vakayı nasıl birleştirebilirim?

| numaralı telefonu denedim, ancak çalışmıyor. Aynı adlandırılır olmasına rağmen tamamen farklı iki yöntem vardır Range.size ve Encoding.size çünkü

case v:Range | v:Encoding 
+0

Hata mı alıyorsunuz?Bazı araştırmalar yaptım ve bu "alternatif modelde değişken ciltleme" türünün Scala tarafından izin verilmeyebileceğini düşünüyorum. Bkz. Https://issues.scala-lang.org/browse/SUGGEST-25 –

+0

Durum v @ (Aralık | Kodlama) => {...} 'çalışır mı? –

cevap

5

Bu çalışamaz. Aynı şey Range.decode ve Edncoding.decode için de geçerlidir. Eğer v.size yazarken

Yani, v tipi o v:Encoding veya v:Range değil v:Encoding|v:Range ya olmalı, bilinen edilmelidir.

Bunu nasıl düzeltebilirim? Böyle bir ortak özelliği yapın:

Sonra Range ve Encoding tanımlarını değiştirin:

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

Şimdi sadece maç fıkrada case v: SomethingWithDecodeAndSize => ... yapabilirsiniz.

Ayrıca ... instance.get'u yapmayın, bu kötü bir tat. Orijinal sınıfların tanımları değiştirmek yapamıyorsanız yerine

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

Güncelleme , bir çıkarıcı kullanabilirsiniz musunuz: Artık

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 
    } 
} 

, kendi maçında case Some(SomethingWithDecodeAndSize(v)) => ... yapabilirsiniz.

1

Alternatif bir çözüm için @ Dima adlı durumda (ve hiçbir gerekli yöntemlerle süpertip yoktur) Range ve Encoding tanımını değiştiremezsiniz: Bize soran lisede ponpon hatırlamak

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

Bu durumda, örtük yerine bir çıkarıcı öneririm. – Dima

1

, "Kazın ne kadar gevşek?"

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 

Cevabın "Gevşek, bebeğim, gevşek" olduğuna inanıyorum.

Düzenleme:

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 

Yani sıradan bir tip sınırları olarak ifade edebiliriz:

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) 
     ^

Hala elbette, yansıtıcı aramanın maliyetini kabul etmeleri gerekir.

+0

Yazı tipi etiketlerini kullanma ve '=: ='? – badcook

+0

Uyarıları dikkate almayın. Onlar önemli ve anlamlı. Ve tam olarak neden burada yaptığını yapmaman gerektiğini açıkla. – Dima

+1

@badcook Bir şey gibi. –