Übersicht
Als Java-Entwickler sind wir vielleicht schon einmal dem Void-Typ begegnet und haben uns gefragt, was sein Zweck ist.
In diesem kurzen Tutorial werden wir etwas über diese besondere Klasse lernen und sehen, wann und wie man sie verwendet und wie man sie möglichst vermeidet.
Was ist der Void-Typ
Seit JDK 1.1 bietet Java den Void-Typ. Sein Zweck ist einfach, den Rückgabetyp void als Klasse darzustellen und einen öffentlichen Wert Class<Void> zu enthalten. Er ist nicht instanzierbar, da sein einziger Konstruktor privat ist.
Daher ist der einzige Wert, den wir einer Void-Variablen zuweisen können, null. Es mag ein wenig nutzlos erscheinen, aber wir werden jetzt sehen, wann und wie man diesen Typ verwendet.
Verwendungen
Es gibt einige Situationen, in denen die Verwendung des Typs Void interessant sein kann.
3.1. Reflexion
Erstens können wir ihn bei der Reflexion verwenden. In der Tat wird der Rückgabetyp jeder void-Methode mit der Void.TYPE-Variablen übereinstimmen, die den bereits erwähnten Class<Void>-Wert enthält.
Stellen wir uns eine einfache Calculator-Klasse vor:
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); }}
Einige Methoden geben eine ganze Zahl zurück, andere geben gar nichts zurück. Nehmen wir nun an, wir müssen durch Reflexion alle Methoden abrufen, die kein Ergebnis zurückgeben. Das erreichen wir, indem wir die Variable Void.TYPE verwenden:
@Testvoid givenCalculator_whenGettingVoidMethodsByReflection_thenOnlyClearAndPrint() { Method calculatorMethods = Calculator.class.getDeclaredMethods(); List<Method> 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()));}
Wie wir sehen, wurden nur die Methoden clear() und print() abgerufen.
3.2. Generics
Eine andere Verwendung des Typs Void ist bei generischen Klassen. Nehmen wir an, wir rufen eine Methode auf, die einen Callable-Parameter benötigt:
public class Defer { public static <V> V defer(Callable<V> callable) throws Exception { return callable.call(); }}
Das Callable, das wir übergeben wollen, muss aber nichts zurückgeben. Daher können wir ein Callable<Void> übergeben:
@Testvoid givenVoidCallable_whenDiffer_thenReturnNull() throws Exception { Callable<Void> callable = new Callable<Void>() { @Override public Void call() { System.out.println("Hello!"); return null; } }; assertThat(Defer.defer(callable)).isNull();}
Wir hätten entweder einen beliebigen Typ (z.B. Callable<Integer>) verwenden und null oder gar keinen Typ (Callable) zurückgeben können, aber die Verwendung von Void macht unsere Absichten deutlich.
Wir können diese Methode auch auf Lambdas anwenden. Tatsächlich hätte unser Callable auch als Lambda geschrieben werden können. Stellen wir uns eine Methode vor, die eine Function benötigt, aber wir wollen eine Function verwenden, die nichts zurückgibt. Dann müssen wir nur dafür sorgen, dass sie Void zurückgibt:
public static <T, R> R defer(Function<T, R> function, T arg) { return function.apply(arg);}
@Testvoid givenVoidFunction_whenDiffer_thenReturnNull() { Function<String, Void> function = s -> { System.out.println("Hello " + s + "!"); return null; }; assertThat(Defer.defer(function, "World")).isNull();}
Wie kann man die Verwendung vermeiden?
Nun haben wir einige Verwendungen des Typs Void gesehen. Auch wenn die erste Verwendung völlig in Ordnung ist, sollten wir die Verwendung von Void in generischen Anwendungen nach Möglichkeit vermeiden. Ein Rückgabetyp, der das Nichtvorhandensein eines Ergebnisses repräsentiert und nur Null enthalten kann, kann in der Tat lästig sein.
Wir werden nun sehen, wie man solche Situationen vermeiden kann. Betrachten wir zunächst unsere Methode mit dem Parameter Callable. Um die Verwendung eines Callable<Void> zu vermeiden, könnten wir eine andere Methode anbieten, die stattdessen einen Runnable-Parameter annimmt:
public static void defer(Runnable runnable) { runnable.run();}
So können wir ein Runnable übergeben, das keinen Wert zurückgibt und so die nutzlose Rückgabe von null loswerden:
Runnable runnable = new Runnable() { @Override public void run() { System.out.println("Hello!"); }};Defer.defer(runnable);
Aber was ist, wenn die Defer-Klasse nicht von uns geändert werden kann? Dann können wir entweder bei der Option Callable<Void> bleiben oder eine andere Klasse erstellen, die ein Runnable nimmt und den Aufruf an die Defer-Klasse verschiebt:
public class MyOwnDefer { public static void defer(Runnable runnable) throws Exception { Defer.defer(new Callable<Void>() { @Override public Void call() { runnable.run(); return null; } }); }}
Damit kapseln wir den lästigen Teil ein für alle Mal in unserer eigenen Methode, was es zukünftigen Entwicklern ermöglicht, eine einfachere API zu verwenden.
Natürlich kann das Gleiche für Function erreicht werden. In unserem Beispiel gibt die Function nichts zurück, also können wir eine andere Methode bereitstellen, die stattdessen einen Consumer annimmt:
public static <T> void defer(Consumer<T> consumer, T arg) { consumer.accept(arg);}
Was aber, wenn unsere Function keinen Parameter annimmt? Wir können entweder ein Runnable verwenden oder unsere eigene funktionale Schnittstelle erstellen (wenn das klarer erscheint):
public interface Action { void execute();}
Dann überladen wir wieder die defer()-Methode:
public static void defer(Action action) { action.execute();}
Action action = () -> System.out.println("Hello!");Defer.defer(action);
Abschluss
In diesem kurzen Artikel haben wir die Java Void-Klasse behandelt. Wir haben gesehen, was ihr Zweck ist und wie man sie verwendet. Wir haben auch einige Alternativen zu ihrer Verwendung kennengelernt.
Wie üblich kann der vollständige Code dieses Artikels auf unserem GitHub gefunden werden.
Erfahren Sie mehr über Spring 5 und Spring Boot 2 mit dem Kurs „Learn Spring“:
>>CHECK OUT THE COURSE