# Лекция 7

## Тема: Анонимные классы, интерфейсы, методы. Лямбда-выражения. Встроенные функциональные интерфейсы. Ссылка на методы и конструкторы.

При создании объектов с помощью оператора new возвращается ссылка на вновь созданный объект. Однако нас никто не обязывает эту ссылку присваивать в качестве значения ссылочной переменной. В таких случаях создается **анонимный объект**. Другими словами, объект есть, а переменной, которая бы содержала ссылку на этот объект, нет.

С практической точки зрения это может выглядеть бесполезным, однако, анонимные объекты требуются довольно часто - обычно в тех ситуациях, когда объект класса используется один раз. Рассмотрим пример:

```java
class PrintManager {
    
    // Различные поля и методы
    
    public void printFile(File file) {
        // Распечатка файла на принтере
    }
}
```

В данном случае, нам нужен объект класса `PrintManager` только для одного действия - для распечатки файла. То есть, мы создаем объект, вызываем метод, после чего объект нам больше не нужен.

В таких случаях удобно использовать анонимные объекты.

```java
File file = new File("file.txt");

// Обычное создание объекта и вызов его метода
PrintManager manager = new PrintManager();
manager.printFile(file);

// Использование анонимного объекта
new PrintManager().printFile(file);

// Использование двух анонимных объектов
new PrintManager().printFile(new File("file.txt"));
```

В первом случае используется обычный порядок работы с объектами - создаем объект класса `PrintManager`, ссылка на объект записывается в ссылочную переменную manager.

Во втором случае мы создаем анонимный объект. Инструкция

```java
new PrintManager().printFile(file);
```

выполняется следующим образом. Сначала создается новый объект класса `PrintManager`, оператор new возвращает ссылку на созданный объект. После чего, мы вызываем метод `printFile()` с аргументом `file`.

В третьем случае, мы в качестве аргумента метода `printFile()` передаем анонимный объект класса `File`. В этом случае, ссылку на объект класса `File` будет хранить параметр метода `printFile()` внутри тела метода.

### Анонимные классы

Механизм анонимных классов позволяет объявить класс и сразу создать его экземпляр. Это позволяет сделать код кратким и выразительным. Анонимные классы удобно использовать, если класс нужен единожды.

Основная особенность - анонимный класс не имеет имени. Анонимный класс может быть подклассом существующего класса или реализацией интерфейса.

Особенности анонимного класса:

* нет явного конструктора;
* к анонимному классу невозможно обратиться извне объявляющего его выражения;
* анонимные классы не могут быть статическими;
* анонимный класс всегда конечен (`final`);
* каждое объявление анонимного класса уникально.

В примере ниже объявлены два анонимный класса, которые являются подклассами `PrintManager`. Оба анонимных класса являются разными уникальными классами.

```java
PrintManager manager1 = new PrintManager() {
    @Override
    public void printFile(File file) {
        super.printFile(file);
    }
};

PrintManager manager2 = new PrintManager() {
    @Override
    public void printFile(File file) {
        super.printFile(file);
    }
};
```

### Анонимные методы (лямбда-выражения)

{% hint style="warning" %}
Из-за особенностей реализации лямбда-выражений в Java, дальнейшая информация по поводу лямбда-выражений справедлива только для языка Java.
{% endhint %}

**Лямбда-выражение** - это анонимный метод, то есть метод без названия. Такой метод выполняется не самостоятельно, а служит для реализации функционального интерфейса. Таким образом, лямбда-выражение приводит к некоторой форме анонимного класса.

Функциональный интерфейс - интерфейс, который содержит один и только один абстрактный метод. Как правило, такой метод определяет предполагаемое назначение интерфейса. Следовательно, функциональный интерфейс представляет единственное действие. К функциональным интерфейсам можно отнести, например, интерфейс ActionListener.

Лямбда-выражения позволяют объявить метод и сразу же использовать его. Это полезно в случаях однократного вызова метода, так как сокращает время на объявление и написание метода без необходимости создавать отдельный класс. Лямбда-выражение в Java имеет следующий синтаксис:

```java
(аргументы) -> (тело выражения)
```

Лямбда-выражение вносит новый элемент в синтаксис и оператор в язык Java. Этот новый оператор называется лямбда-оператором, или операцией "стрелка" `->`. Он разделяет лямбда-выражение на две части. В левой части указываются любые параметры, требующиеся в лямбда-выражении (если параметры не требуются, то они указываются пустым списком). А в правой части находится тело лямбда-выражения, где указываются действия, выполняемые лямбда-выражением. Операция `->` буквально означает "становиться" или "переходить".

В Java определены две разновидности тел лямбда-выражений. Одна из них состоит из единственного выражения (**одиночное выражение**), а другая - из блока кода (**блочное выражение**).

#### **Одиночное лямбда-выражение**.

Для начала рассмотрим самое простое лямбда-выражение. Это будет выражение, которое не принимает никаких параметров, а возвращает константу. Также объявим метод, который аналогичен лямбда-выражению.

```java
() -> 99

public double foo() {
    return 99;
}
```

Приведем еще один пример уже более полезного выражения

```java
() -> Math.random() * 100

public double foo() {
    return Math.random() * 100;
}
```

Как вам понятно, лямбда-выражение возвращает псевдослучайное значение, умноженное на 100.

Два первых примера не принимают никаких параметров. Если же вам необходимо передать параметры, они указываются списком в левой части лямбда-оператора.

```java
(a, b) -> a * b

public double foo(int a, int b) {
    return a * b;
}
```

Тип параметров можно указывать явно, но зачастую в этом нет необходимости, потому что тип параметров выводится.

#### Блочные лямбда-выражения

Предыдущие примеры содержали в теле выражения лишь одно выражение. Мы уже знаем, что такие лямбда-выражения называются одиночными.

Но ничего вам не мешает создавать блочные лямбда-выражения, которые в теле содержат блок выражений. В таком блоке можно создавать циклы, ветвления - все как в обычном блоке выражений.

При создании блочного выражения необходимо явно указать оператор `return`.

## Функциональные интерфейсы. Ссылки на методы и конструкторы.

#### Лямбда-выражение как реализация интерфейса

Как было упомянуто ранее, в Java 8 было введено понятие функционального интерфейса - интерфейса с одним абстрактным методом. Лямбда-выражение не существует самостоятельно, а реализует абстрактный метод, определенный в функциональном интерфейсе.

Таким образом, лямбда может быть указана в том контексте, в котором определен ее целевой тип. Один из таких контекстов создается в том случае, когда лямбда присваивается ссылке на функциональный интерфейс. К числу других контекстов целевого типа относится инициализация переменных, оператор return и аргументы методов.

{% hint style="danger" %}
Данная тема находится в процессе оформления материала.
{% endhint %}

