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: