Функциональные и анонимные классы. Конструктор анонима

Функциональным классом называют класс, у которого нет состояния, есть только поведение. Технически это значит, что из полей ему позволено иметь лишь константы.

Функциональный класс

Представьте, что нам понадобилось отслеживать события, которые происходят с определённым объектом. Например, звонки на определённый номер телефона. Для этого нужно, чтобы телефон умел отчитываться об этих событиях: когда происходит событие, нужно вызывать метод.

Поддержка слушателей в Nexus5:

package online.javanese.basics.oop.functionalClass;

import java.util.Random;
import java.util.UUID;

public class Nexus5 extends MobilePhone {
    
    // конструкторы — без изменений

    // объект, который будет получать уведомления о звонках
    private OnDialListener onDialListener;
    
    // возможность передать этот объект
    public void setOnDialListener(OnDialListener listener) {
        onDialListener = listener;
    }
    
    @Override
    public void dial(String number) {
        System.out.println("Звоним на " + number);
        
        // если слушатель есть, уведомляем его о звонке
        if (onDialListener != null) {
            onDialListener.onDial(number);
        }
    }
    
    // слушатель должен унаследовать этот абстрактный класс
    // и выполнить свои действия в onDial
    public abstract class OnDialListener {
        public abstract void onDial(String number);
    }
    
}

Пример использования:

    public static void main(String[] args) {

        class MyListener extends Nexus5.OnDialListener {
            @Override
            public void onDial(String number) {
                if (number.equals("+7 900 777-77-77")) {
                    System.out.println("Одни семёрки в номере!");
                }
            }
        }

        Nexus5 nexus = new Nexus5();
        nexus.setOnDialListener(new MyListener());
        nexus.dial("+7 900 123-45-67");
        nexus.dial("+7 900 777-77-77");
        nexus.dial("+7 911 000-00-00");
    }
Звоним на +7 900 123-45-67
Звоним на +7 900 777-77-77
Одни семёрки в номере!
Звоним на +7 911 000-00-00

Анонимный функциональный класс

Можно не объявлять класс явно:

        Nexus5 nexus = new Nexus5();
        nexus.setOnDialListener(new OnDialListener() {
            @Override
            public void onDial(String number) {
                if (number.equals("+7 900 777-77-77")) {
                    System.out.println("Одни семёрки в номере!");
                }
            }
        });
        nexus.dial("+7 900 123-45-67");
        nexus.dial("+7 900 777-77-77");
        nexus.dial("+7 911 000-00-00");
Примечание

В примере все действия производятся с конкретным классом Nexus5. А вот таким образом можно распространить эту функциональность на всю иерархию Phone:

package online.javanese.basics.oop.functionalClass;

public abstract class Phone {

    // слушатель
    private OnDialListener onDialListener;
    // и его сеттер
    public void setOnDialListener(OnDialListener listener) {
        onDialListener = listener;
    }

    // метод dial финален, чтобы гарантировать,
    // что ни один из наследников (MobilePhone,
    // Nokia3310, Nexus5) не могли переопределить это поведение
    public final void dial(String number) {
        performDial(number);
        if (onDialListener != null) {
            onDialListener.onDial(number);
        }
    }
    // конкретные модели телефонов должны иметь свой способ звонить
    protected abstract void performDial(String number);

    // абстракция для слушателей
    public static abstract class OnDialListener {
        public abstract void onDial(String number);
    }
}
package online.javanese.basics.oop.functionalClass;
// весь класс без изменений
public abstract class MobilePhone extends Phone {

    protected final String serialNumber;
    protected final String imei;

    protected int batteryVoltage = 3900;

    public MobilePhone(String serialNumber, String imei) {
        this.serialNumber = serialNumber;
        this.imei = imei;
    }

    public final String getDisplayVoltage() {
        return batteryVoltage + " мВ";
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() +
                "(serialNumber=" + serialNumber +
                ", IMEI=" + imei +
                ", batteryVoltage=" + batteryVoltage + "mV)";
    }

}
package online.javanese.basics.oop.functionalClass;

import java.util.Random;
import java.util.UUID;

public class Nexus5 extends MobilePhone {
    
    // конструкторы без изменений
    public Nexus5() {
        this(UUID.randomUUID().toString(), Long.toString(new Random().nextLong()));
    }
    
    public Nexus5(String serialNumber, String imei) {
        super("NEX-" + serialNumber, imei);
    }

    // просто звоним
    @Override
    public void performDial(String number) {
        System.out.println("Звоним на " + number);
    }

}

Конструктор анонимного класса

Анонимный класс тоже может иметь конструктор. Только у него нет имени (как и у класса) и собственного набора аргументов. Вот так это выглядит:


public class Nexus5 extends MobilePhone {

    // тут всё по-старому
    
    public static void main(String[] args) {
        Nexus5 nexus = new Nexus5();
        nexus.setOnDialListener(new OnDialListener() {

            {   // это конструктор
                System.out.println("new " + getClass());
            }

            @Override
            public void onDial(String number) {
                if (number.equals("+7 900 777-77-77")) {
                    System.out.println("Одни семёрки в номере!");
                }
            }
        });
    }
}

Выводится:

new class online.javanese.basics.oop.functionalClass.Nexus5$1

Технически анонимный класс имеет имя Nexus5$1. Компилятор генерирует анонимному классу имя вида НаружныйКласс$Номер.

Комментарии к уроку

Сообщить об ошибке