Рубрики
Без рубрики

Тип Void в Java

Узнайте, как использовать тип Java Void и когда следует избегать использования типа Void.

Автор оригинала: François Dupire.

1. Обзор

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

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

2. Что такое тип пустоты

Начиная с JDK 1.1, Java предоставляет нам тип Void . Его цель-просто представить возвращаемый тип void в виде класса и содержать Class public value. Он не является инстанцируемым, так как его единственный конструктор является частным.

Поэтому единственное значение, которое мы можем присвоить переменной Void , – это null . Это может показаться немного бесполезным, но теперь мы увидим, когда и как использовать этот тип.

3. Обычаи

Есть некоторые ситуации, когда использование типа Void может быть интересным.

3.1. Рефлексия

Во-первых, мы могли бы использовать его при отражении. Действительно, возвращаемый тип любого void метода будет соответствовать Void.ТИП переменная, содержащая класс значение, упомянутое ранее .

Давайте представим себе простой Калькулятор класс:

public class Calculator {
    private int result = 0;

    public int add(int number) {
        return result += number;
    }

    public int sub(int number) {
        return result -= number;
    }

    public void clear() {
        result = 0;
    }

    public void print() {
        System.out.println(result);
    }
}

Некоторые методы возвращают целое число, некоторые ничего не возвращают. Теперь предположим, что мы должны получить путем рефлексии все методы, которые не возвращают никакого результата . Мы достигнем этого, используя Void.ТИП переменная:

@Test
void givenCalculator_whenGettingVoidMethodsByReflection_thenOnlyClearAndPrint() {
    Method[] calculatorMethods = Calculator.class.getDeclaredMethods();
    List calculatorVoidMethods = Arrays.stream(calculatorMethods)
      .filter(method -> method.getReturnType().equals(Void.TYPE))
      .collect(Collectors.toList());

    assertThat(calculatorVoidMethods)
      .allMatch(method -> Arrays.asList("clear", "print").contains(method.getName()));
}

Как мы видим, были извлечены только методы clear() и print () .

3.2. Дженерики

Другое использование типа Void – с универсальными классами. Предположим, что мы вызываем метод, для которого требуется параметр Callable :

public class Defer {
    public static  V defer(Callable callable) throws Exception {
        return callable.call();
    }
}

Но … Подлежащий выкупу мы хотим, чтобы передача не должна ничего возвращать. Таким образом, мы можем пройти Вызываемый :

@Test
void givenVoidCallable_whenDiffer_thenReturnNull() throws Exception {
    Callable callable = new Callable() {
        @Override
        public Void call() {
            System.out.println("Hello!");
            return null;
        }
    };

    assertThat(Defer.defer(callable)).isNull();
}

Мы могли бы либо использовать случайный тип (например, Callable ) и возвращать null , либо вообще не использовать тип ( Callable) , но использование Void ясно указывает на наши намерения.

Мы также можем применить этот метод к лямбдам. На самом деле наш Callable мог бы быть записан как лямбда. Давайте представим себе метод , требующий Функции , но мы хотим использовать функцию , которая ничего не возвращает. Тогда нам просто нужно заставить его вернуться Void :

public static  R defer(Function function, T arg) {
    return function.apply(arg);
}
@Test
void givenVoidFunction_whenDiffer_thenReturnNull() {
    Function function = s -> {
        System.out.println("Hello " + s + "!");
        return null;
    };

    assertThat(Defer.defer(function, "World")).isNull();
}

4. Как избежать Его использования?

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

Теперь посмотрим, как избежать подобных ситуаций. Сначала рассмотрим наш метод с параметром Callable . Чтобы избежать использования Вызываемого , мы могли бы предложить другой метод, принимающий вместо него параметр Runnable :

public static void defer(Runnable runnable) {
    runnable.run();
}

Таким образом, мы можем передать ему Runnable , который не возвращает никакого значения, и таким образом избавиться от бесполезного return null :

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello!");
    }
};

Defer.defer(runnable);

Но тогда что делать, если класс Defer не наш для модификации? Тогда мы можем либо придерживаться опции Callable , либо создать другой класс, взяв Runnable и отложив вызов на Defer класс :

public class MyOwnDefer {
    public static void defer(Runnable runnable) throws Exception {
        Defer.defer(new Callable() {
            @Override
            public Void call() {
                runnable.run();
                return null;
            }
        });
    }
}

Делая это, мы инкапсулируем громоздкую часть раз и навсегда в наш собственный метод, позволяя будущим разработчикам использовать более простой API.

Конечно, то же самое может быть достигнуто для Функции . В нашем примере функция | ничего не возвращает, поэтому мы можем предоставить другой метод, принимающий вместо него Потребителя :

public static  void defer(Consumer consumer, T arg) {
    consumer.accept(arg);
}

Тогда что, если наша функция не принимает никаких параметров? Мы можем либо использовать Runnable , либо создать свой собственный функциональный интерфейс (если это кажется более понятным):

public interface Action {
    void execute();
}

Затем мы снова перегружаем метод defer() :

public static void defer(Action action) {
    action.execute();
}
Action action = () -> System.out.println("Hello!");

Defer.defer(action);

5. Заключение

В этой короткой статье мы рассмотрели класс Java Void . Мы видели, в чем его предназначение и как им пользоваться. Мы также узнали некоторые альтернативы его использованию.

Как обычно, полный код этой статьи можно найти на нашем GitHub .