Аннотации используются для передачи метаданных при объявлении. Например, аннотация @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:
Contents
- Введение
- Основы
- Единобразие типов
- Классы
- Значения Параметров По умолчанию
- Именованные Аргументы
- Трейты
- Кортежи
- Композиция классов с трейтами
- Функции Высшего Порядка
- Вложенные Методы
- Множественные списки параметров (Каррирование)
- Классы Образцы
- Сопоставление с примером
- Объекты Одиночки
- Регулярные Выражения
- Объект Экстрактор
- Сложные for-выражения
- Обобщенные Классы
- Вариантность
- Верхнее Ограничение Типа
- Нижнее Ограничение Типа
- Внутренние классы
- Члены Абстрактного Типа
- Составные Типы
- Самоописываемые типы
- Контекстные параметры, также известные, как неявные параметры
- Неявные Преобразования
- Полиморфные методы
- Выведение Типа
- Операторы
- Вызов по имени
- Аннотации
- Пакеты и Импорт
- Объекты Пакета