[英]Is is possible to capture the type parameter of a trait using Manifests in Scala 2.7.7?
我在Scala中編寫了一個ServletUnitTest特性,為ServletUnit提供了一個方便的API。 我有類似以下內容:
/**
* Utility trait for HttpUnit/ServletUnit tests
*
* @param [T] Type parameter for the class under test
*/
trait ServletUnitTest[T <: HttpServlet] {
/**
* Resource name of the servlet, used to construct the servlet URL.
*/
val servletName: String
/**
* Servlet class under test
*/
implicit val servletClass: Manifest[T]
/**
* ServletUnit {@link ServletRunner}
*/
sealed lazy val servletRunner: ServletRunner = {
val sr = new ServletRunner();
sr.registerServlet(servletName, servletClass.erasure.getName);
sr
}
/**
* A {@link com.meterware.servletunit.ServletUnitClient}
*/
sealed lazy val servletClient = servletRunner.newClient
/**
* The servlet URL, useful for constructing WebRequests
*/
sealed lazy val servletUrl = "http://localhost/" + servletName
def servlet(ic: InvocationContext) = ic.getServlet.asInstanceOf[T]
}
class MyServletTest extends ServletIUnitTest[MyServlet] {
val servletName = "download"
// ... test code ...
}
這段代碼沒有按照書面編譯,但希望我的意圖很明確。 有沒有辦法做到這一點(有或沒有清單)?
在研究這個主題時,我在 Jorge Ortiz的這個scala-list帖子中發現了一個解決方案,它為我做了訣竅,並且比Aaron更簡單。 從本質上講,他的解決方案是(釋義):
trait A[T] {
implicit val t: Manifest[T]
}
class B[T: Manifest] extends A[T] {
override val t = manifest[T]
}
(我在2011年寫這篇文章時忽略了OP請求是2.7.7兼容的......)
目前,Scala將特征表示為接口,因此該技術將起作用。 但是,這種方法實現traits存在一些問題,因為當將方法添加到trait時,實現類不一定會重新編譯,因為接口表示只有一個轉發方法指向另一個實際實現該方法的類。 為了應對這種情況,今年早些時候有人談到在運行時使用接口注入到JVM來解決這個問題。 如果使用這種方法的權力,那么特征的類型信息將會丟失,然后才能捕獲它。
可以使用Java反射API訪問類型信息。 它不漂亮,但它的工作原理:
trait A[T]{
def typeParameter = {
val genericType = getClass.getGenericInterfaces()(0).asInstanceOf[ParameterizedType]
genericType.getActualTypeArguments()(0)
}
}
class B extends A[Int]
new B().typeParameter -> java.lang.Integer
應該添加一些不變檢查我只實現了快樂的路徑。
我找到了一個有效的解決方案,但它非常尷尬,因為它要求測試類在評估任何trait的lazy val之前調用trait上的方法(clazz)。
/**
* Utility trait for HttpUnit/ServletUnit tests
*
* @param [T] Type parameter for the class under test
*/
trait ServletUnitTest[T <: HttpServlet] {
/**
* Resource name of the servlet, used to construct the servlet URL.
*/
val servletName: String
/**
* Servlet class under test
*/
val servletClass: Class[_] // = clazz
protected def clazz(implicit m: Manifest[T]) = m.erasure
/**
* ServletUnit {@link ServletRunner}
*/
sealed lazy val servletRunner: ServletRunner = {
val sr = new ServletRunner();
sr.registerServlet(servletName, servletClass.getName);
sr
}
/**
* A {@link com.meterware.servletunit.ServletUnitClient}
*/
sealed lazy val servletClient = servletRunner.newClient
/**
* The servlet URL, useful for constructing WebRequests
*/
sealed lazy val servletUrl = "http://localhost/" + servletName
def servlet(ic: InvocationContext) = ic.getServlet.asInstanceOf[T]
}
class MyServletTest extends ServletIUnitTest[MyServlet] {
val servletName = "download"
val servletClass = clazz
// ... test code ...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.