繁体   English   中英

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

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

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