1. 概要

このチュートリアルでは、メタプログラミングのためにScalaでアノテーションを使用する方法を見ていきます。

2. 注釈

注釈は、さまざまな動作を追加できるプログラミング言語機能です。 一般的な動作には、追加のコンパイル時チェック、コード生成、または単なるメタ情報が含まれます。 注釈付きのクラスがより頻繁に見られますが、変数、型、および式にも注釈を付けることができます。

@deprecated("Use D", "1.0") class C { ... } // Class annotation
@transient @volatile var m: Int             // Variable annotation
String @local                               // Type annotation
(e: @unchecked) match { ... }               // Expression annotation

2.1. 正確性を確保するための注釈

Scalaは2種類の注釈をサポートしています。 最初のタイプは、コンパイル時チェックを追加するアノテーションです。 このタイプの注釈は、特定の条件が満たされた場合に実際にコンパイルが失敗する原因となる可能性があります。 一般的な例は、 @tailrec アノテーションです。これにより、メソッドが末尾再帰になります。 リストの長さを計算するための2つの再帰メソッドを定義することから始めましょう。1つは末尾再帰で、もう1つはそうではありません。

def recursiveLength(list: List[String]): Long = list match {
  case Nil => 0
  case head :: tail => 1 + recursiveLength(tail)
}

def tailRecursiveLength(list: List[String], accumulator: Long): Long = list match {
  case Nil => accumulator
  case head :: tail => tailRecursiveLength(tail, accumulator + 1)
}

どちらの方法も問題なく機能します。 ただし、微妙なバグのため、最初の関数は末尾再帰ではありません。 ここでは、Scalaアノテーションが役立ちます。 @tailrec アノテーションを追加すると、コンパイラはコードのコンパイルに失敗します。

scala> @tailrec
     | def recursiveLength(list: List[String]): Long = list match {
     |         case Nil => 0
     |         case head :: tail => 1 + recursiveLength(tail)
     |     }
4 |        case head :: tail => 1 + recursiveLength(tail)
  |                                 ^^^^^^^^^^^^^^^^^^^^^
  |                 Cannot rewrite recursive call: it is not in tail position

独自のアノテーションを作成することで、必要に応じてさらに多くのコンパイル時チェックを追加できます。

2.2. コード生成に影響を与える注釈

2番目のタイプの注釈はコードを生成するものです。 たとえば、 @inline scalaアノテーションは、パフォーマンスを向上させるためにメソッドバイトコードの生成方法を変更します。

@inline 
final def sum(x: Int, y: Int): Int = 
  x + y

2.3. ユーザー定義の注釈

開発者は、コードベースで新しい注釈を定義することもできます。 そのために、から拡張する新しいクラスを作成する必要があります scala.annotation.StaticAnnotation また scala.annotation.ClassfileAnnotation。 最初のものはコンパイラに表示されますが、クラスファイルにも実行時にも表示されません。 後者は、注釈付きのクラスに視覚的に保存されますが、実行時に保持されません。

scala> class MyAnnotation extends scala.annotation.ClassfileAnnotation

@MyAnnotation class Bar

3. 結論

この記事では、最も一般的なメタプログラミング手法の1つであるアノテーションについて説明しました。 この言語機能を使用すると、コンパイラ機能を拡張したり、コードベースに配置された単純な注釈からコードを生成したりできます。