2012-04-12 16 views
6

Trong Scala 2.9.1Generic thu thập bằng cách gõ vào scala

Với

def collectFirstOfT[T](la: List[_])(implicit m:Manifest[T]) : Option[T] = { 
    la.collect{case x if m.erasure.isAssignableFrom(x.getClass) => x}. 
    headOption.asInstanceOf[Option[T]]} 

class A 
class B 

tại sao biểu hiện này:

val oB:Option[B] = collectFirstOf(List(new A,new B)) 

biên dịch nhưng thu thập Một số (A), nhưng

val oB =collectFirstOf[B](List(new A,new B)) 

hoạt động tốt.

Cách suy ra T từ Tùy chọn [T]?

Trả lời

3

Bạn phải nhìn vào dòng sau như hai phần riêng biệt, phía bên tay trái của = và quyền:

val oB: Option[B] = collectFirstOf(List(new A,new B)) 

gì bạn đang mong đợi ở đây là kiểu của biểu thức collectFirstOf (rvalue) nên được suy ra từ loại giá trị oB. Trình biên dịch không thể làm điều này. Bạn phải nói cụ thể loại bạn đang mong đợi. Lấy ví dụ sau đây:

val v: Long = 1 + 4 

Kiểu của biểu thức 1 + 4 là một Int. Int này sau đó được chuyển đổi thành Long. Trình biên dịch không, và không thể phỏng đoán rằng bạn muốn 1 hoặc 4 là Long:

Vì vậy, để khắc phục sự cố của bạn, bạn cần phải cho trình biên dịch biết loại bạn đang mong đợi, nếu không nó sẽ giả định java.lang.Object:

val oB = collectFirstOf[B](List(new A,new B)) 

Vì vậy tệp kê khai được gán đúng và tất cả đều tốt với thế giới. Vậy tại sao sau thậm chí biên dịch:

val oB:Option[B] = collectFirstOfT(List(new A,new B)) 
oB: Option[B] = Some([email protected]) 

ngay từ cái nhìn đầu tiên, nó không có vẻ như thế này nên làm việc, nhưng nó. Điều này là do collectFirstOfT thực sự trả về một lựa chọn [Không có gì], mà có thể được chuyển đổi một cách an toàn đến một lựa chọn [B]:

scala> val f = collectFirstOfT(List(new A,new B)) 
f: Option[Nothing] = Some([email protected]) 

scala> f.asInstanceOf[Option[B]] 
res4: Option[B] = Some([email protected]) 
+0

Rất thú vị! Làm thế nào tôi có thể ngăn chặn các chức năng bị bỏ lỡ? (Ý tưởng, nó không nên biên dịch) – jwinandy

+0

Một cách nhanh chóng và dễ dàng là thêm đối số rõ ràng, lớp: collectFirstOfT [T] (cls: Class [T], la: List [_]), sau đó gọi như sau: collectFirstOfT (classOf [B], Danh sách (mới A, B mới)). Điều này sẽ trả về một Option [B] như mong đợi. –

0

Bởi vì trình biên dịch không thể suy ra được T từ các thuật toán, vì vậy bạn phải viết rõ ràng. Trong trường hợp đầu tiên, collect chấp nhận tất cả danh sách.

+0

'T' (hoặc' Tùy chọn [T] ') chỉ dành cho loại trả về. '{case x if m.erasure.isAssignableFrom (x.getClass) => x}' là một phần 'PartialFunction [-Any, + Any]'. – jwinandy

3

này:

val oB:Option[B] = collectFirstOfT(List(new A,new B)) 

là tương đương với điều này:

val oB: Tùy chọn [B] = collectFirstOfT [Không có gì] (Danh sách (mới A, mới B))

là một phân lớp của mọi thứ, sau đó nó có thể được gán từ A. Than ôi, nó cũng có thể được gán từ B, có nghĩa là bạn có thể chỉ định một Option[Nothing] cho một Option[B].

Thực tế thú vị: đó là sự thật bởi vì Option là cùng một biến thể. Nếu không, thì T sẽ phải được suy ra là B, điều này sẽ làm cho nó hoạt động.

Thực tế thú vị 2: mã này không biên dịch trên thân cây của ngày hôm qua.