2010-12-23 3 views
4

У меня есть абстрактный абстрактный класс Java и несколько подклассов, которые содержат аннотацию на уровне класса под названием @DBTable. Я пытаюсь искать иерархию классов для аннотаций с использованием хвостовой рекурсией метода Scala:Ошибка аннотации Scala tailrec

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 аннотированного метода: он называется рекурсивно с различными аргументами типа ". Что не так с моим внутренним методом?

Спасибо.

ответ

15

Это связано с тем, как компилятор реализует хвостовую рекурсию по циклам. Это делается как один шаг в цепочке преобразований из байт-кодов Scala в Java. Каждое преобразование должно создать программу, которая снова будет правильной. Тем не менее, вы не можете изменить тип переменных при выполнении в середине цикла, поэтому компилятор не может расшириться в цикл с правильной настройкой.

+1

Спасибо за объяснение. Кстати, отличный язык! – Ralph

+1

Я также буду замечать: какое точное точное сообщение об ошибке! «Не удалось оптимизировать метод @tailrec annotated: он называется рекурсивно с разными аргументами типа». Тот, кто пишет такие драгоценные камни, должен быть одним классным котом. – extempore

2

Поскольку параметр типа B и его оценка не строго обязательно, вы можете использовать экзистенциальный тип вместо,

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

Scala принимает это определение для хвостовых рекурсии вызовов.

+0

Спасибо. Я попробую. Вы знаете, почему первая форма была отклонена? – Ralph

+0

@Ralph: Нет, я не знаю точно. Оптимизация @tailrec преобразует рекурсивную функцию в цикл и с стиранием я не вижу, как это может вызвать проблемы. Это может быть ограничение реализации компилятора, или может быть, что спецификация не позволяет этого, потому что целевая платформа может стирать такие типы, как JVM. –

3

Могу ли я предложить более краткий вариант кода?

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

}

+0

Ницца! Я изменю свой код. – Ralph