[英]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.