2010-01-01 14 views
19

Bu yazdırma neden wtf? Kalıp eşleşmesi yapısal tiplerde çalışmıyor mu?Scala'daki yapı tiplerine uygun desen

"hello" match { 
    case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?") 
    case _ => println("okie dokie") 
    } 
+0

Senin yorumuna :) ışığında benim cevap genişlettik. –

cevap

18

(scala -unchecked) üzerinde işaretlenmemiş uyarılarıyla Scala yorumlayıcı bu örneği Koşu aşağıdaki uyarıyı üretir: warning: refinement AnyRef{def doesNotExist(Int,List[_]): Double} in type pattern is unchecked since it is eliminated by erasure. Ne yazık ki, böyle bir jenerik tip JVM'nin rejenere jenerikleri olmadığı için çalışma zamanında kontrol edilemez. JVM bu model maçında gördüğü

tek şey:

"hello" match { 
    case s: Object => ... 
    case annon: Object => ... 
} 

DÜZENLEME: yorumlarınızı yanıt olarak, bir çözüm düşünüyordum ama dün göndermeye zaman yoktu . Ne yazık ki, çalışması yapsa bile, derleyici düzgün Manifest enjekte edemiyor.

çözmek istediğiniz problem nesneye belirli bir yapısal tipte ise karşılaştırmaktır. İşte

type Foo = AnyRef { def doesNotExist(i: Int, x: List[_]): Double } 

def getManifest[T](implicit m: Manifest[T]) = m 

def isFoo[T](x: T)(implicit mt: Manifest[T]) = 
    mt == getManifest[Foo] 

Yöntem isFoo (Scala 2.7.6.final üzerimde birkaç kez çöktü gibi benzer fikirlerle oynarken, Scala 2.8-r20019) Ben düşünüyordum bazı kod temelde bir manifestoyu karşılaştırır Foo'un x sınıfı. İdeal bir dünyada, bir yapısal tipin tezahürü, gerekli yöntemleri içeren herhangi bir tipin tezahürüne eşit olmalıdır. En azından bu benim düşüncem. getManifest[Foo] çağrılırken derleyici yerine Manifest[Foo] bir Manifest[AnyRef] enjekte Maalesef bu, derlemek başarısız olur. İlginç bir şekilde, eğer yapısal bir tip kullanmıyorsanız (örneğin, type Foo = String), bu kod beklendiği gibi derler ve çalışır. Bir noktada, bunun neden yapısal tiplerle başarısız olduğunu görmek için bir soru yayınlayacağım - bu bir tasarım kararı mı, yoksa sadece deneysel yansıma API'sinin bir problemi. Bu gerçekleşmediği takdirde

, her zaman bir nesne bir yöntem içerip içermediğini görmek için Java yansıması kullanabilirsiniz.

containsMethod("foo", "concat", classOf[String]) // true 
containsMethod("foo", "bar", classOf[List[Int]]) // false 

... ama çok güzel değil: çalışır

def containsMethod(x: AnyRef, name: String, params: java.lang.Class[_]*) = { 
    try { 
    x.getClass.getMethod(name, params: _*) 
    true 
    } 
    catch { 
    case _ => false 
    } 
} 

beklendiği gibi.

Ayrıca, yapısal tip yapısı zamanında mevcut olmadığını not edin. def foo(x: {def foo: Int}) = x.foo numaralı bir yönteminiz varsa, silme işleminden sonra def foo(x: Object) = [some reflection invoking foo on x] alırsınız, tür bilgisi kaybolur. Bu nedenle, ilk bakışta yansıma kullanılır, çünkü Object üzerinde bir yöntemi çağırmanız gerekir ve JVM, Object'un bu yönteme sahip olup olmadığını bilmez. Eğer yansıma kullanmak zorunda gidiyoruz

+0

Teşekkürler, Flaviu. Bu sorumu cevaplıyor. Ama yine de, bunu başarmanın en iyi yolunun ne olacağını merak etmeme neden oluyor çünkü yapı, çalışma zamanında yansıma yoluyla elde edilebilen bir şey. Ona ulaşmak sadece sakar. –

+0

Takip için teşekkürler. Çok ilginç şeyler. –

7

, en az bir aspiratör ile daha güzel görünmesini sağlayabilirsiniz:

object WithFoo { 
    def foo(){ 
     println("foo was called") 
    } 
} 

object HasFoo { 
    def containsMethod(x: AnyRef, name: String, params: Array[java.lang.Class[_]]) : Boolean = { 
     try { 
      x.getClass.getMethod(name, params: _*) 
      true 
     } catch { 
      case _ => false 
     } 
    } 

    def unapply(foo:AnyRef):Option[{def foo():Unit}] = { 
     if (containsMethod(foo, "foo", new Array[Class[_]](0))) { 
      Some(foo.asInstanceOf[{def foo():Unit}]) 
     } else None 
    } 
} 


WithFoo.asInstanceOf[AnyRef] match { 
    case HasFoo(foo) => foo.foo() 
    case _ => println("no foo") 
} 
+0

Eğer HasFoo'yu daha esnek bir şekilde tanımlayabiliyor olsaydı, “val HasFoo = new Has [{def foo(): Unit}] (" foo ")' gibi.Ben sadece bu şekilde yapmaya çalıştım, ama yine de {def foo (i: Int): Int} gibi daha karmaşık türlerle ilgili bazı problemler var gibi görünüyor. – Debilski

+0

Derleyici neden bunu otomatik olarak yapmıyor? – Gabriel

+0

'içerirMethod', 'Dene (x.getClass.getMethod (name, params: _ *))' a dehidrate edilebilir. IsSuccess' –