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

时间:2009-12-18 20:50:44

标签: scala servletunit

我正在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 ...
}

此代码不按编写方式编译,但希望我的意图很明确。有没有办法做到这一点(有或没有清单)?

4 个答案:

答案 0 :(得分:2)

在研究这个主题时,我找到了Jorge Ortiz的解决方案in this scala-list post,它为我做了诀窍,并且比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兼容...)

答案 1 :(得分:1)

目前,Scala将特征表示为接口,因此该技术可行。但是,这种方法实现traits存在一些问题,因为当将方法添加到trait时,实现类不一定会重新编译,因为接口表示只有一个转发方法指向另一个实际实现该方法的类。为了应对这种情况,今年早些时候有人谈到在运行时使用接口注入到JVM来解决这个问题。如果使用这种方法的权力,那么特征的类型信息将在您捕获之前丢失。

答案 2 :(得分:1)

可以使用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

应该添加一些不变检查我只实现了快乐的路径。

答案 3 :(得分:0)

我找到了一个有效的解决方案,但它非常尴尬,因为它要求测试类在评估任何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 ...
}