Scala tailrec annotation error

I have an abstract Java class called ImmutableEntity and several subclasses that contain class level annotation called @DBTable . I am trying to search for class hierarchy for annotation using the Scala tail recursive method:

  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) } } } 

When I compile this code, I get the error: "the @tailrec annotated method could not be optimized: it is called recursively with different type arguments." What is wrong with my internal method?

Thanks.

+4
source share
3 answers

This is due to the way the compiler implements tail recursion in cycles. This is done as one step in the conversion chain from Scala to Java bytecodes. Each conversion should create a program that again enters the correct type. However, you cannot change the type of variables when executed in the middle of a loop, so the compiler cannot expand as a loop.

+15
source

Can I suggest a more concise version of the code?

 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
source

Since the type B parameter and its binding are not strictly required, you can use the existential type instead,

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

Scala accepts this definition for tail recursive calls.

+2
source

Source: https://habr.com/ru/post/1333217/


All Articles