Tour of Scala

Аннотации

Language

Аннотации используются для передачи метаданных при объявлении. Например, аннотация @deprecated перед объявлением метода, заставит компилятор вывести предупреждение, если этот метод будет использован.

object DeprecationDemo extends App { @deprecated("deprecation message", "release # which deprecates method") def hello = "hola" hello } 
object DeprecationDemo extends App: @deprecated("deprecation message", "release # which deprecates method") def hello = "hola" hello 

Такой код скомпилируется, но компилятор выдаст предупреждение: “there was one deprecation warning”.

Аннотация применяется к первому идущему после нее объявлению или определению. Допускается использование сразу нескольких аннотаций следующих друг за другом. Порядок, в котором приводятся аннотации, не имеет значения.

Аннотации, обеспечивающие корректность работы кода

Некоторые аннотации приводят к невозможности компиляции, если условие (условия) не выполняется. Например, аннотация @tailrec гарантирует, что метод является хвостовой рекурсией. Хвостовая рекурсия помогает держать потребление памяти на постоянном уровне. Вот как она используется в методе, который вычисляет факториал:

import scala.annotation.tailrec def factorial(x: Int): Int = { @tailrec def factorialHelper(x: Int, accumulator: Int): Int = { if (x == 1) accumulator else factorialHelper(x - 1, accumulator * x) } factorialHelper(x, 1) } 
import scala.annotation.tailrec def factorial(x: Int): Int = @tailrec def factorialHelper(x: Int, accumulator: Int): Int = if x == 1 then accumulator else factorialHelper(x - 1, accumulator * x) factorialHelper(x, 1) 

Метод factorialHelper имеет аннотацию @tailrec, которая гарантирует, что метод действительно является хвостовой рекурсией. Если бы мы изменили реализацию factorialHelper так как указано далее, то компиляция бы провалилась:

import scala.annotation.tailrec def factorial(x: Int): Int = { @tailrec def factorialHelper(x: Int): Int = { if (x == 1) 1 else x * factorialHelper(x - 1) } factorialHelper(x) } 
import scala.annotation.tailrec def factorial(x: Int): Int = @tailrec def factorialHelper(x: Int): Int = if x == 1 then 1 else x * factorialHelper(x - 1) factorialHelper(x) 

Мы бы получили сообщение “Recursive call not in tail position”(Рекурсивный вызов не в хвостовой позиции).

Аннотации, влияющие на генерацию кода

Некоторые аннотации типа @inline влияют на сгенерированный код (т.е. в результате сам код вашего jar-файл может отличаться). Такая аннотация означает вставку всего кода в тело метода вместо вызова. Полученный байт-код длиннее, но, надеюсь, работает быстрее. Использование аннотации @inline не гарантирует, что метод будет встроен, но заставит компилятор сделать это, если и только если будут соблюдены некоторые разумные требования к размеру сгенерированного кода.

Некоторые аннотации типа @main влияют на сгенерированный код (т.е. в результате сам код вашего jar-файл может отличаться). Аннотация @main к методу создает исполняемую программу, которая вызывает метод как точку входа.

Java аннотации

Есть некоторые отличия синтаксиса аннотаций, если пишется Scala код, который взаимодействует с Java.

Примечание:Убедитесь, что вы используете опцию -target:jvm-1.8 с аннотациями Java.

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

@interface Source { public String url(); public String mail(); } 

А затем использовать следующим образом

@Source(url = "https://coders.com/", mail = "[email protected]") public class MyJavaClass extends TheirClass ... 

Использование аннотации в Scala похоже на вызов конструктора. Для создания экземпляра из Java аннотации необходимо использовать именованные аргументы:

@Source(url = "https://coders.com/", mail = "[email protected]") class MyScalaClass ... 

Этот синтаксис достаточно перегруженный, если аннотация содержит только один элемент (без значения по умолчанию), поэтому, если имя указано как value, оно может быть применено в Java с помощью конструктора-подобного синтаксиса:

@interface SourceURL { public String value(); public String mail() default ""; } 

А затем можно использовать следующим образом

@SourceURL("https://coders.com/") public class MyJavaClass extends TheirClass ... 

В этом случае Scala предоставляет такую же возможность

@SourceURL("https://coders.com/") class MyScalaClass ... 

Элемент mail был указан со значением по умолчанию, поэтому нам не нужно явно указывать его значение. Мы не можем смешивать эти два стиля в Java:

@SourceURL(value = "https://coders.com/", mail = "[email protected]") public class MyJavaClass extends TheirClass ... 

Scala обеспечивает большую гибкость в этом отношении

@SourceURL("https://coders.com/", mail = "[email protected]") class MyScalaClass ... 

Contributors to this page: