简体   繁体   English

是否可以使用Scala 2.7.7中的Manifest捕获特征的类型参数?

[英]Is is possible to capture the type parameter of a trait using Manifests in Scala 2.7.7?

I'm writing a ServletUnitTest trait in Scala to provide a convenience API for ServletUnit . 我在Scala中编写了一个ServletUnitTest特性,为ServletUnit提供了一个方便的API。 I have something like the following in mind: 我有类似以下内容:

/**
 * 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 ...
}

This code doesn't compile as written, but hopefully my intent is clear. 这段代码没有按照书面编译,但希望我的意图很明确。 Is there a way to do this (with or without Manifests)? 有没有办法做到这一点(有或没有清单)?

While researching this topic, I found about a solution in this scala-list post by Jorge Ortiz, which did the trick for me, and is simpler than Aaron's. 在研究这个主题时,我 Jorge Ortiz的这个scala-list帖子中发现了一个解决方案,它为我做了诀窍,并且比Aaron更简单。 In essence, his solution is (paraphrasing): 从本质上讲,他的解决方案是(释义):

  trait A[T] {
    implicit val t: Manifest[T]
  }

  class B[T: Manifest] extends A[T] {
    override val t = manifest[T]
  }

(I'm ignoring the OP request to be 2.7.7 compatible as I'm writing this in 2011...) (我在2011年写这篇文章时忽略了OP请求是2.7.7兼容的......)

For now, Scala represents traits as interfaces so this technique will work. 目前,Scala将特征表示为接口,因此该技术将起作用。 There are some problems with this approach to implementing traits, however, in that when methods are added to a trait, the implementing class will not necessarily recompile because the interface representation only has a forwarding method pointing to another class that actually implements the method concretely. 但是,这种方法实现traits存在一些问题,因为当将方法添加到trait时,实现类不一定会重新编译,因为接口表示只有一个转发方法指向另一个实际实现该方法的类。 In response to this there was talk earlier this year of using interface injection into the JVM at runtime to get around this problem. 为了应对这种情况,今年早些时候有人谈到在运行时使用接口注入到JVM来解决这个问题。 If the powers that be use this approach then the trait's type information will be lost before you can capture it. 如果使用这种方法的权力,那么特征的类型信息将会丢失,然后才能捕获它。

The type information is accessible with the Java reflection API. 可以使用Java反射API访问类型信息。 It's not pretty but it works: 它不漂亮,但它的工作原理:

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

Some invariant checks should be added I've only implemented the happy path. 应该添加一些不变检查我只实现了快乐的路径。

I found a solution that works, but it's pretty awkward since it requires the test class to call a method (clazz) on the trait before any of the trait's lazy vals are evaluated. 我找到了一个有效的解决方案,但它非常尴尬,因为它要求测试类在评估任何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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM