2012-07-03 54 views
1

假設,我們有一個抽象類和抽象類型字段:如何區分具有不同抽象類型字段的對象?

abstract class A {type T} 

現在讓我們假設,我們有一個方法,它返回A類型的對象,但類型字段T可能會有所不同。我們如何區分這些對象?

我們可以嘗試進行模式匹配:

object Test { 
    def tryToDistinguish(a: A) = 
    a match { 
     case b: A {type T = String} => println("String type") 
     case b: A {type T = Int} => println("Int type") 
     case b: A => println("Other type") 
    } 
} 

但是,編譯器會抱怨:

$>scalac -unchecked Test.scala 
Test.scala:8: warning: refinement example.test.A{type T = String} in type patter 
n example.test.A{type T = String} is unchecked since it is eliminated by erasure 

     case b: A {type T = String} => println("String type") 
     ^
Test.scala:9: warning: refinement example.test.A{type T = Int} in type pattern e 
xample.test.A{type T = Int} is unchecked since it is eliminated by erasure 
      case b: A {type T = Int} => println("Int type") 
      ^
two warnings found 

看來類型字段的類型將被擦除被刪除(側問題:因爲類型字段被轉換爲Java中的參數類型?)

因此,這將不起作用:

scala> Test.tryToDistinguish(new A {type T = Int}) 
String type 

替代方案:我們可以alternativley創建一個枚舉,爲了區分放置物體的附加字段中的類A。但是,這味道,因爲這意味着我們重新實現類型系統。

問題:有沒有辦法區分不同類型的對象類型的幫助下類型字段?如果不是,那麼什麼是一個好的解決方法?

回答

2

您可以使用<%<,它在編譯時會告訴您兩種類型是否兼容。

scala> class A { type T } 
defined class A 

scala> implicitly[A { type T=String } <%< A { type T=Int } ] 
<console>:9: error: could not find implicit value for parameter e: <%<[A{type T = String},A{type T = Int}] 
       implicitly[A { type T=String } <%< A { type T=Int } ] 
         ^

scala> implicitly[A { type T=String } <%< A { type T=String } ] 
res1: <%<[A{type T = String},A{type T = String}] = <function1> 

scala> implicitly[A { type T=String } <%< A ] 
res3: <%<[A{type T = String},A] = <function1> 
+0

謝謝。如何調用<%<'?我想在Scala文檔中閱讀更多關於它的內容。 –

+0

同樣在這裏,你能解釋一下''<<<'''和''隱式''的直觀語義是什麼? –

+0

該文檔http://www.scala-lang.org/api/current/index.html#scala.Predef$$$less$colon$less – pedrofurla

2

正如您猜測的那樣,抽象類型成員是一個編譯時間信息,將在類型擦除期間被擦除。這是一個使用implicit參數的解決方法。調度是靜態的。

scala> class A { 
    | type T 
    | } 
defined class A 

scala> implicit def distString(a: A { type T = String }) = "String" 
distString: (a: A{type T = String})java.lang.String 

scala> implicit def distInt(a: A { type T = Int }) = "Int" 
distInt: (a: A{type T = Int})java.lang.String 

scala> implicit def distOther[O](a: A { type T = O }) = "Other" 
distOther: [O](a: A{type T = O})java.lang.String 

scala> def distinguish(a: A)(implicit ev: A { type T = a.T } => String) = ev(a) 
distinguish: (a: A)(implicit ev: A{type T = a.T} => String)String 

scala> distinguish(new A { type T = String }) 
res2: String = String 

scala> distinguish(new A { type T = Int }) 
res3: String = Int 

scala> distinguish(new A { type T = Float }) 
res4: String = Other 

另一種方式:

scala> def dist(a: A)(implicit s: a.T =:= String = null, i: a.T =:= Int = null) = 
    | if (s != null) "String" else if (i != null) "Int" else "Other" 
dist: (a: A)(implicit s: =:=[a.T,String], implicit i: =:=[a.T,Int])String 

scala> dist(new A { type T = String }) 
res5: String = String 

scala> dist(new A { type T = Int }) 
res6: String = Int 

scala> dist(new A { type T = Float }) 
res7: String = Other 

編輯:

如果上述方案不能滿足你,你要具體化和後反思在運行這類信息所有,你也可以使用稱爲Manifest s的東西來做到這一點。

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

abstract class A { 
    type T 
    def man: Manifest[T] 
} 

object A { 
    def apply[X](implicit m: Manifest[X]) = new A { type T = X; def man = m } 
} 


// Exiting paste mode, now interpreting. 

defined class A 
defined module A 

scala> def disti(a: A): String = a match { 
    | case _ if a.man <:< manifest[String] => "String" 
    | case _ if a.man <:< manifest[Int] => "Int" 
    | case _ => "Other" 
    | } 
disti: (a: A)String 

scala> disti(A.apply[String]) 
res14: String = String 

scala> disti(A.apply[Int]) 
res15: String = Int 

scala> disti(A.apply[Float]) 
res16: String = Other 
1

側的問題:由於類型字段轉換爲參數類型 在Java中?

字節碼中沒有類型參數,所以類型字段不可能被翻譯成它們。類型字段僅在編譯期間存在。

替代方案:我們可以替代地創建一個枚舉並在類A中添加一個 附加字段以區分這些對象。 但這個味道,因爲這意味着,我們重新實施類型 系統。

在運行時只有一種類型的問題:這是什麼類? Scala 2.10提供了更多信息,但它仍然歸結爲這個問題。

如果你確實繼承A,你可以通過反射的信息(在2.10)。否則,我不這麼認爲 - 我已經在REPL上進行過測試,並且它不起作用,但編譯到類文件的東西有更多關於它們的信息。

任何其他問題都必須提出有關值。

問題:在類型字段的幫助下,對象類型 有什麼不同嗎?如果不是的話,什麼是好的 解決方法?

不,除非斯卡拉2.10,反射和子類。良好的解決方法?使用值。儘管如此,處理它的通常方式並不是枚舉,但Manifest可以隱式生成。

查找questions about how to get around type erasure in Scala

+0

那麼確定類型字段必須以某種方式表示在編譯的類文件中?否則,當它們出現在已編譯的庫中時,我會如何引用它們?必須有一些信息包含名稱和邊界,否? –

+0

@Sciss有。如果你在Scala生成的類文件上使用'javap -s',你會看到一個常量ScalaSig,後面跟着一個帶有該類型的二進制包表示的常量。缺少的是解碼它的能力。至於匿名班是否有這些信息,我不知道。 –

相關問題