当asInstanceOf用于理解时的ClassCastException

时间:2015-05-29 01:35:50

标签: scala playframework classcastexception for-comprehension

在PlayFramework 2.4中,我尝试将所有控制器方法转换为JavaScript路径。

val jsRoutesClass = classOf[routes.javascript]
val controllers = jsRoutesClass.getFields.map(_.get(null))

for (
  controller <- controllers;
  method <- controller.getClass.getDeclaredMethods
) yield method.invoke(controller).asInstanceOf[JavaScriptReverseRoute]

但发生以下错误:

Error injecting constructor, java.lang.ClassCastException: java.lang.String cannot be cast to play.api.routing.JavaScriptReverseRoute 
at controllers.Application.<init>(Application.scala:21)
while locating controllers.Application
   for parameter 1 at router.Routes.<init>(Routes.scala:35)
while locating router.Routes
while locating play.api.inject.RoutesProvider
while locating play.api.routing.Router

我添加了一些代码,但我认为这是不必要的代码。在此之后,不会发生异常。

for (
  controller <- controllers;
  method <- controller.getClass.getDeclaredMethods;
  action <- method.invoke(controller).toString
) yield method.invoke(controller).asInstanceOf[JavaScriptReverseRoute]

为什么错误出现在第一个代码示例中而不出现在第二个代码示例中?

1 个答案:

答案 0 :(得分:4)

让我们一步一步看看代码,看看每行产生的结果。 我将您的代码转换为完整的示例代码,但我希望我能够捕获代码的本质。

package controllers

import play.api._
import play.api.mvc._

class Sample extends Controller {
  def hello(name: String) = Action {
    implicit req =>
    import routes.javascript._

    val jsRoutesClass = classOf[routes.javascript]
    val controllers = jsRoutesClass.getFields.map(_.get(null))
    val met = for (
        controller <- controllers;
        method <- controller.getClass.getDeclaredMethods
      ) yield method
    Ok(met.mkString(", "))
  }
}

执行此请求时,您会看到类似

的内容
public play.api.routing.JavaScriptReverseRoute controllers.javascript.ReverseSample.hello(), public java.lang.String controllers.javascript.ReverseSample._defaultPrefix()

您应该从路径中找到所有方法,但请注意,还有_defaultPrefix()方法,返回类型为String。

这是您的第一个代码示例不起作用的原因。其中一种方法不会返回JavaScriptReverseRoute,因此会抛出异常。

这仍然无法解释为什么您的第二个代码示例不起作用。所以让我们在Sample Controller中添加一些代码:

package controllers

import play.api._
import play.api.mvc._

class Sample extends Controller {
  def hello(name: String) = Action {
    implicit req =>
    import routes.javascript._

    val jsRoutesClass = classOf[routes.javascript]
    val controllers = jsRoutesClass.getFields.map(_.get(null))
    val met = for (
        controller <- controllers;
        method <- controller.getClass.getDeclaredMethods
      ) yield method.invoke(controller)
    Ok(met.mkString(", "))
  }
}

请注意,我们尚未转换方法调用的结果,并且请求产生类似于:

的内容
JavaScriptReverseRoute(controllers.Sample.hello,
    function(name) {
      return _wA({method:"GET", url:"/" + (function(k,v) {return v})("name", encodeURIComponent(name))})
    }
  ), 

仔细观察,你会看到最后有一个流氓,意味着我们临时val met的值在位置0的JavaScriptReverseRoute和位置1以及空字符串。

因此,查看您的解决方法,操作action <- method.invoke(controller).toString是一个带有javascript一次的字符串,另一次是一个空字符串。由于我们处于理解状态,因此String会自动转换为字符数组,如果此数组为空,则不会执行yield块。

您的解决方法的问题是,如果_.defaultPrefix()产生一个字符串,它将再次抛出一个类强制转换异常。

更好的解决方案是过滤每个没有例外结果类型的方法,如下所示:

package controllers

import play.api._
import play.api.mvc._

class Sample extends Controller {
  def hello(name: String) = Action {
    implicit req =>
      import routes.javascript._

      val jsRoutesClass = classOf[routes.javascript]
      val controllers = jsRoutesClass.getFields.map(_.get(null))
      val met = for (
          controller <- controllers;
          method <- controller.getClass.getDeclaredMethods if method.getReturnType() == classOf[play.api.routing.JavaScriptReverseRoute]
         ) yield  method.invoke(controller).asInstanceOf[play.api.routing.JavaScriptReverseRoute]
       Ok(met.mkString(", "))
  }
}

这应该导致预期的行为。