2010-03-02 73 views
4

我想向scala.Enumeration添加一個方法。我的第一個方法是試圖擴展它,但被this咬了。我的第二種方法是嘗試定義一個方法,並傳入枚舉 - 如果這有效,我希望使用隱式轉換。然而,我很難用保留類型的方法返回類型。引用方法簽名中的枚舉值類型

object EnumExample { 
    object SampleEnum extends Enumeration { 
    val include, exclude = Value 
    } 

    def parse[T <: Enumeration](name:String, enum:T):T#Value = 
    enum.valueOf(name) match { 
     case Some(x) => x 
     case x => throw new RuntimeException("No field named '" + name + "' found on enum " + enum + ", legal values = " + enum.values) 
    } 

    def main(args:Array[String]) = { 
    //compiles fine, and preserves custom type 
    val withNameExample:SampleEnum.Value = SampleEnum.withName("include") 

    //also fine, but we lost type info 
    val enumWithHash:Enumeration#Value = parse("include", SampleEnum) 

    /** 
    error: type mismatch; 
    found : Main.$anon.EnumExample.SampleEnum#Value 
    required: Main.$anon.EnumExample.SampleEnum.Value 
     val parseExample:SampleEnum.Value = parse("include", SampleEnum) 
    * 
    */ 
    val customTypeWithHash:SampleEnum.type#Value = parse("include", SampleEnum) 

    //same error 
    val customTypeWithDot:SampleEnum.Value = parse("include", SampleEnum) 
    } 
} 

一個明顯的問題是隻從parse方法中刪除返回類型聲明,但這給了我一個「非法依賴方法類型」。這給我留下了很多問題:

  1. 這是可以指定?不管怎樣,我想在從字符串中解析枚舉字段時得到一個很好的錯誤信息。

  2. 爲什麼我會得到「非法依賴方法類型」?

  3. 在這種情況下,「#」運算符(?)究竟是什麼?

+0

碰到這個問題,試過了,但是我在'enum.valueOf(name)match {'說'value的valueOf不是類型參數T的成員'的編譯錯誤。那麼爲什麼這樣呢?我使用Scala 2.9.1。 – zapadlo 2013-01-07 10:15:02

+1

我認爲他們在2.9中刪除了valueOf - 用withName代替。更好的是,我推薦這裏描述的密封traits /案例對象:http://stackoverflow.com/questions/4236251/should-i-prefer-sealed-class-or-enumeration-in-scala。這種方法有它自己的疣,但根據我的經驗,它更有用。 – 2013-01-07 12:57:29

回答

5

這看起來像一個bug(至少在我測試過的2.8.0 Beta1中)。

特別有啓發是:

scala> var x: SampleEnum.type#Value = null 
x: SampleEnum.Value = null 

在這裏,我們請求的任意內型,但我們實際上得到具體內的類型。這只是打破了(如果沒有一個,我會提交一個錯誤報告,除非其他人迅速解釋爲什麼這不是一個錯誤)。

那麼,該怎麼辦?嗯,首先,讓我們來了解解析的原始方法簽名:

def parse[T <: Enumeration](name:String, enum:T):T#Value 

我們T這是Enumeration一個子類,enum這是T一個實例, - 因爲沒有辦法表達的是, Value必須從那個特定實例T,我們必須求助於T#Value(即內部類型的T,而不考慮它來自哪個特定的T)。

現在我們必須傳遞一個特定的對象,取回一個通用的內部對象,並在ExampleObject.type#Value的表面上執行它,它與ExampleObject.Value相同,即使它們的輸入方式不同。

因此,我們必須從頭開始編寫自己的對象:

class SampleWorkaroundClass extends Enumeration { 
    val include, exclude = Value 
} 
lazy val SampleWorkaround = new SampleWorkaroundClass 

在這裏,我們有一個專門定義類的單實例。現在,我們可以得到解決的bug在Object

scala> val typeWorks:SampleWorkaroundClass#Value = parse("include",SampleWorkaround) 
typeWorks: SampleWorkaroundClass#Value = include 

(該lazy val只是爲了得到完全相同的行爲,因此與他們沒有實例化,直到他們使用的對象;但僅靠一個val將被罰款。)


編輯:Outer#Inner裝置「的任何內部類類型Inner從該外類未來的」,而不是myOuter.Inner這意味着「僅該類Inner類型的,其具有的OutermyOuter這種情況下,作爲其包圍類」。此外,我沒有在2.8.0 Beta1中獲得依賴類型錯誤 - 但無法指定類型使事情變得非常尷尬。


編輯:bug報告更新 - 它現在的工作方式顯然是故意的。要以這種方式使用的類型,你的目的是明確指定函數調用的類型(因爲它不推斷爲你想要的),像這樣

val suggested: SampleEnum.type#Value = parse[SampleEnum.type]("include",SampleEnum) 

這種方式是比較容易,如果你只需要這樣做幾次。如果您必須多次執行此操作,則使用val(或lazy val)實例化創建自己的類可能會使事情變得更簡單/更緊湊。

+0

好多了!我建立在你的推薦和通過隱式參數擺脫第二個參數: def parse [T <:Enumeration](name:String)(implicit m:Manifest [T]):T#Value = { val enum = (name)匹配{0} .get(null).asInstanceOf [T] //哇,yuck case「Some(x)=> x case x => throw new RuntimeException(」在枚舉上找到沒有名稱的字段'「+ name +」'enum +「,legal values =」+ enum.values) } } 有沒有更好的方法從它的類對象中獲取對象實例? – 2010-03-03 14:35:08

+0

恐怕我不清楚清單是否知道你的方法是否可行。請記住,所有這些清單擺弄都會讓代碼變慢。通常這是無關緊要的,但如果您打算使用它來完成解析大型文本文件的主要工作,則可能需要使用不同的設計。 – 2010-03-03 15:51:31