簡體   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