2010-12-23 15 views
4

ImmutableEntityというJava抽象クラスと、@DBTableというクラスレベルのアノテーションを含むいくつかのサブクラスがあります。私はこのコードをコンパイルすると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アノテーション付きメソッドを最適化することができませんでした」:それは呼ばれて、私は末尾再帰Scalaの方法を使用して、注釈のクラス階層を検索しようとしています再帰的に異なる型引数を持つ "。私の内側の方法に何が問題なのですか?

ありがとうございました。

答えて

15

コンパイラがループごとにテール再帰を実装する方法が原因です。これは、ScalaからJavaのバイトコードへの一連の変換の一歩として行われます。それぞれの変換は、もう一度型が正しいプログラムを生成する必要があります。しかし、中間ループ実行で変数の型を変更することはできません。そのため、コンパイラは型正しいループに拡張できませんでした。

+1

説明をありがとう。いい言葉! – Ralph

+1

私はまた、観察する:どのような細かい、正確なエラーメッセージ! "@tailrecアノテーション付きメソッドを最適化できませんでした:異なる型引数を使用して再帰的に呼び出されます"。そのような宝石を書いている人は、誰もがクールな猫でなければなりません。 – extempore

2

typeパラメータ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