【问题标题】:Scala tailrec annotation errorScala tailrec注释错误
【发布时间】:2010-12-23 16:53:21
【问题描述】:

我有一个名为ImmutableEntity 的Java 抽象类和几个包含名为@DBTable 的类级别注释的子类。我正在尝试使用尾递归 Scala 方法在类层次结构中搜索注释:

  def getDbTableForClass[A <: ImmutableEntity](cls: Class[A]): String = {
    @tailrec
    def getDbTableAnnotation[B >: A](cls: Class[B]): DBTable = {
      if (cls == null) {
        null
      } else {
        val dbTable = cls.getAnnotation(classOf[DBTable])
        if (dbTable != null) {
          dbTable
        } else {
          getDbTableAnnotation(cls.getSuperclass)
        }
      }
    }

    val dbTable = getDbTableAnnotation(cls)
    if (dbTable == null) {
      throw new
              IllegalArgumentException("No DBTable annotation on class " + cls.getName)
    } else {
      val value = dbTable.value
      if (value != null) {
        value
      } else {
        throw new
                IllegalArgumentException("No DBTable.value annotation on class " + cls.getName)
      }
    }
  }

当我编译此代码时,我收到错误消息:“无法优化 @tailrec 注释方法:使用不同类型的参数递归调用它”。我的内部方法有什么问题?

谢谢。

【问题讨论】:

    标签: scala annotations tail-recursion


    【解决方案1】:

    这是因为编译器通过循环实现尾递归的方式。这是从 Scala 到 Java 字节码的一系列转换中的一个步骤。每个转换都必须生成一个类型正确的程序。但是,在循环执行过程中你不能改变变量的类型,这就是为什么编译器不能扩展成一个类型正确的循环。

    【讨论】:

    • 感谢您的解释。顺便说一句,很棒的语言!
    • 我还会观察到:多么精细、精确的错误信息! “无法优化 @tailrec 注释方法:它使用不同类型的参数递归调用”。写出这样的宝石的人一定是一只很酷的猫。
    【解决方案2】:

    我可以推荐一个更简洁的代码版本吗?

    def getDbTableForClass[A <: ImmutableEntity](cls: Class[A]): String = {
    @tailrec
    def getDbTableAnnotation[B >: A](cls: Class[B]): DBTable = cls match {
      case null => null
      case c if c.isAnnotationPresent(classOf[DBTable]) => c.getAnnotation(classOf[DBTable])
      case other => getDbTableAnnotation(other.getSuperclass)
    }
    
    getDbTableAnnotation(cls) match {
      case null => throw new IllegalArgumentException("No DBTable annotation on class " + cls.getName)
      case dbTable if dbTable.value ne null => dbTable.value
      case other => throw new IllegalArgumentException("No DBTable.value annotation on class " + cls.getName)
    }
    

    }

    【讨论】:

      【解决方案3】:

      由于类型参数B 及其绑定不是严格要求,您可以使用存在类型代替,

      @tailrec
      def getDbTableAnnotation(cls: Class[_]): DBTable = {
        ...
      }
      

      Scala 接受尾递归调用的这个定义。

      【讨论】:

      • 谢谢。我会试试的。你知道为什么第一个表格被拒绝了吗?
      • @Ralph:不,我不确定。 @tailrec 优化将递归函数转换为循环,并且通过擦除我看不出这会如何造成麻烦。这可能是编译器实现的限制,也可能是规范不允许这样做,因为目标平台可能会擦除 JVM 等类型。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-04
      • 2011-01-23
      • 1970-01-01
      相关资源
      最近更新 更多