Можно ли захватить параметр типа черты, используя Манифесты в Scala 2.7.7? - PullRequest
1 голос
/ 18 декабря 2009

Я пишу черту ServletUnitTest в Scala, чтобы предоставить удобный API для ServletUnit . Я имею в виду что-то вроде следующего:

/**
 * 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 ]

2 голосов
/ 15 февраля 2011

Исследуя эту тему, я нашел решение в этом сообщении scala-list Хорхе Ортиса, которое помогло мне и проще, чем решение Аарона. По сути, его решение (перефразируя):

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

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

(я игнорирую запрос OP на совместимость с 2.7.7, так как пишу это в 2011 году ...)

1 голос
/ 22 декабря 2009

Информация о типе доступна с помощью API отражения Java. Это не красиво, но работает:

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

Должны быть добавлены некоторые инвариантные проверки. Я реализовал только счастливый путь.

1 голос
/ 19 декабря 2009

Пока что Scala представляет черты как интерфейсы, поэтому этот метод будет работать. Однако в этом подходе к реализации признаков есть некоторые проблемы, связанные с тем, что при добавлении методов в признак класс реализации не обязательно перекомпилируется, поскольку представление интерфейса имеет только метод пересылки, указывающий на другой класс, который фактически реализует метод конкретно. В ответ на это в начале этого года говорилось об использовании внедрения интерфейса в JVM во время выполнения, чтобы обойти эту проблему. Если полномочия используют этот подход, то информация о типе черты будет потеряна, прежде чем вы сможете ее захватить.

0 голосов
/ 23 декабря 2009

Я нашел решение, которое работает, но оно довольно неловко, так как требует, чтобы тестовый класс вызывал метод (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 ...
}
...