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

是否可以使用Scala 2.7.7中的清单捕获特征的类型参数?,scala,servletunit,Scala,Servletunit,我在Scala中编写了一个ServletUnitTest特性,以提供一个方便的API。我有如下想法: /** * Utility trait for HttpUnit/ServletUnit tests * * @param [T] Type parameter for the class under test */ trait ServletUnitTest[T <: HttpServlet] { /** * Resource name of the servle

我在Scala中编写了一个ServletUnitTest特性,以提供一个方便的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 ...
}

这段代码并不像编写的那样编译,但希望我的意图是明确的。有没有一种方法可以在有或没有清单的情况下实现这一点?

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

目前,Scala将特征表示为接口,因此这种技术将起作用。但是,这种实现trait的方法存在一些问题,当方法添加到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

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

类型信息可以通过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

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

我找到了一个可行的解决方案,但它非常棘手,因为它要求测试类在计算特征的任何延迟VAL之前调用该特征的方法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 ...
}

我找到了一个可行的解决方案,但这相当尴尬,因为它要求测试类在计算trait的任何延迟vals之前调用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 ...
}

在研究这个话题的时候,我发现了一个由豪尔赫·奥尔蒂斯提出的解决方案,这个解决方案为我做到了这一点,而且比艾伦的更简单。 从本质上讲,他的解决方案是:

  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兼容的要求…

在研究这个主题时,我发现了Jorge Ortiz的一个解决方案,它为我解决了这个问题,比Aaron的更简单。 从本质上讲,他的解决方案是:

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

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

我在2011年写这篇文章时忽略了与2.7.7兼容的OP请求…

我可能错了,但我认为,由于清单是由编译器提供的,所以无论trait是如何实现的,我的技术都可以工作。另一方面,基于运行时反射的技术可能会遇到麻烦。我猜这就是为什么现在所有这些都没有正式记录下来,因为底层的实现可能会在您身上发生变化。我可能错了,但我认为,由于清单是由编译器提供的,所以无论trait是如何实现的,我的技术都会起作用。另一方面,基于运行时反射的技术可能会遇到麻烦。我猜这就是为什么现在所有这些都没有正式记录下来,因为底层的实现可能会在您身上发生变化。我想擦除会使使用反射变得不可能,但我猜不会,因为测试类没有参数化。谢谢我原以为擦除会使反射无法使用,但我想不会,因为测试类没有参数化。谢谢