2010-03-12 73 views
4

我開始使用後端使用Hibernate(JPA)的Scala應用程序。爲了加載一個對象,我使用下面這行代碼:scala的通用對象加載函數

val addr = s.load(classOf[Address], addr_id).asInstanceOf[Address]; 

不用說,這有點痛苦。我寫了一個輔助類,它是這樣的:

import org.hibernate.Session 

class DataLoader(s: Session) { 
    def loadAddress(id: Long): Address = { 
    return s.load(classOf[Address], id).asInstanceOf[Address]; 
    } 
    ... 
} 

所以,現在我可以這樣做:

val dl = new DataLoader(s) 
val addr = dl loadAddress(addr_id) 

這裏的問題:我如何寫一個可以加載任何一個通用的參數化方法使用相同模式的對象?即

val addr = dl load[Address](addr_id) 

(或類似的規定。)

我是新來斯卡拉所以請原諒這裏任何東西,是特別可怕的。

回答

5

這就是:

import org.hibernate.Session 
class DataLoader(s: Session) { 
    def load[A](id: Long)(implicit m: Manifest[A]): A = { 
    return s.load(m.erasure, id).asInstanceOf[A]; 
    } 
} 

編輯 - 或者,以確保任何鑄造錯誤 - 因爲休眠返回錯了對象的結果 - 會發生內部load,就像這樣:

import org.hibernate.Session 
class DataLoader(s: Session) { 
    def load[A](id: Long)(implicit m: Manifest[A]): A = { 
    return m.erasure.asInstanceOf[Class[A]].cast(s.load(m.erasure, id)); 
    } 
} 

斯卡拉2。8,你也可以寫這樣的:

import org.hibernate.Session 
class DataLoader(s: Session) { 
    def load[A : Manifest](id: Long): A = { 
    return s.load(manifest[A].erasure, id).asInstanceOf[A]; 
    } 
} 

你可以用一個隱含的會議結合起來,以及通過Chris的建議:

def load[A](id: Long)(implicit m: Manifest[A], s: org.hibernate.Session): A = { 
    return s.load(m.erasure, id).asInstanceOf[A]; 
} 

請注意,您不能結合上下文視圖符號( A : Manifest)以及其他隱式參數。

+0

@丹尼爾 - 你的asInstance演員陣容恐怕不是很安全。考慮兩個語句'val a = load [Address](1)'和'val b:Any = load [Address](2)''的情況。在第二種情況下,實際上不會發生對Address類的強制轉換(它們被擦除),並且您相信Hibernate會實際返回一個正確類型的實例(公平地說,它肯定會)。 – 2010-03-13 14:05:46

+0

@oxbow我明白了。你的觀點是,'Class.cast'會在'load'內引起運行時錯誤,而不是留下一些可能會在以後引起麻煩的事情? – 2010-03-13 21:22:49

+0

是的 - 就是這樣。在這裏進行的唯一「正確」轉換是在load方法之外,並由編譯器插入,因爲引用「a」的類型是Address。如果調用'load'方法並將其分配給'Any',那麼不會發生CCE,並且您可能會發現某行發生了什麼事情。 – 2010-03-14 12:35:36

3

一種方法是做利用java.lang.Class.cast方法,以做類似:

def load[A](clazz: Class[A], id: Long)(implicit s: Session) : A 
     = clazz.cast(s.load(clazz, id)) 

然後用法如下:

implicit val s = ...//get hibernate session 
val addr = load(classOf[Address], 1) 

這不是從什麼巨大的差別你已經爲了訪問class實例,你需要通過它。

我很自信不能安全你想Manifests什麼,因爲他們不能在編譯時,你才能需要劇組工作提供參數化Class[Address](他們只能提供擦除Class[_])。我沒有看到任何其他機構做從Manifest

鑄造當然你也可以使用asInstanceOf[A]代替cast但是這被在編譯時擦除isInstanceOf[Object],因此沒用在編譯時類型檢查的條款(因此是不可取的)。

+0

有趣。這會讓事情變得更加整潔......但是,您剛剛教會了我幾個新的Scala構造。感謝。 – 2010-03-12 15:57:21

+1

哈!我會證明你錯了! ;-) – 2010-03-12 17:49:01

+0

順便說一下...'asInstanceOf [A]'被擦除?這沒有任何意義。此外,刪除發生在運行時,而不是編譯時。另一方面,我從來沒有想過使用'Class [A]'來投射到'A'。有趣。 – 2010-03-12 18:02:52