Leere Geben Sie Java ein

1. Übersicht

Als Java-Entwickler sind wir möglicherweise gelegentlich auf den Void- Typ gestoßen und haben uns gefragt, wozu er dient.

In diesem kurzen Tutorial lernen wir diese besondere Klasse kennen und sehen, wann und wie man sie verwendet und wie man sie nach Möglichkeit vermeidet.

2. Was ist der Void - Typ

Seit JDK 1.1 stellt uns Java den Void- Typ zur Verfügung. Sein Zweck ist es einfach den darstellen Leeren Rückgabetyp als eine Klasse und einen enthält Klasse öffentlichen Wert. Es ist nicht instanziierbar, da sein einziger Konstruktor privat ist.

Daher ist der einzige Wert, den wir einer Void- Variablen zuweisen können, null . Es mag ein bisschen nutzlos erscheinen, aber wir werden jetzt sehen, wann und wie dieser Typ verwendet wird.

3. Verwendungen

Es gibt einige Situationen, in denen die Verwendung des Void- Typs interessant sein kann.

3.1. Reflexion

Erstens könnten wir es verwenden, wenn wir nachdenken. Tatsächlich der Rückgabetyp jeder Lücke wird Methode , das Spiel Void.TYPE Variable, die hält Klasse Wert bereits erwähnt .

Stellen wir uns eine einfache Rechnerklasse 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 Ganzzahl zurück, andere geben nichts zurück. Nehmen wir nun an , wir müssen alle Methoden, die kein Ergebnis liefern, durch Reflexion abrufen . Dies erreichen wir mit der Variablen Void.TYPE :

@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())); }

Wie wir sehen können, wurden nur die Methoden clear () und print () abgerufen.

3.2. Generika

Eine andere Verwendung des Void- Typs sind generische Klassen. Nehmen wir an, wir rufen eine Methode auf, für die ein Callable- Parameter erforderlich ist:

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

Aber der Callable , den wir weitergeben möchten, muss nichts zurückgeben. Daher können wir ein Callable übergeben :

@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(); }

Wir hätten entweder einen zufälligen Typ (z. B. Callable ) verwenden und null oder gar keinen Typ ( Callable) zurückgeben können , aber die Verwendung von Void gibt unsere Absichten klar an.

Wir können diese Methode auch auf Lambdas anwenden. Tatsächlich hätte unser Callable als Lambda geschrieben werden können. Stellen wir uns eine Methode vor, die eine Funktion erfordert , aber wir möchten eine Funktion verwenden , die nichts zurückgibt. Dann müssen wir es nur noch Void zurückgeben lassen :

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. Wie vermeide ich es?

Jetzt haben wir einige Verwendungen vom Typ Void gesehen . Selbst wenn die erste Verwendung völlig in Ordnung ist, möchten wir möglicherweise die Verwendung von Void in Generika nach Möglichkeit vermeiden . In der Tat kann es umständlich sein , auf einen Rückgabetyp zu stoßen, der das Fehlen eines Ergebnisses darstellt und nur null enthalten kann.

Wir werden jetzt sehen, wie wir diese Situationen vermeiden können. Betrachten wir zunächst unsere Methode mit dem Parameter Callable . Um die Verwendung eines Callable zu vermeiden, bieten wir möglicherweise eine andere Methode an, die stattdessen einen Runnable- Parameter verwendet:

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

Wir können es also als Runnable übergeben, das keinen Wert zurückgibt und so die nutzlose Rückgabe null beseitigt :

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 Callable- Option bleiben oder eine andere Klasse erstellen, die eine Runnable nimmt und den Aufruf an die Defer- Klasse zurückstellt :

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

Auf diese Weise kapseln wir den umständlichen Teil ein für alle Mal in unserer eigenen Methode, sodass zukünftige Entwickler eine einfachere API verwenden können.

Das Gleiche kann natürlich auch für die Funktion erreicht werden . In unserem Beispiel gibt die Funktion nichts zurück, daher können wir stattdessen eine andere Methode für einen Consumer bereitstellen :

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

Was ist dann, wenn unsere Funktion keine Parameter akzeptiert? Wir können entweder ein Runnable verwenden oder eine eigene Funktionsschnittstelle erstellen (wenn dies klarer erscheint):

public interface Action { void execute(); }

Dann überladen wir die Methode defer () erneut:

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

5. Schlussfolgerung

In diesem kurzen Artikel haben wir die Java Void- Klasse behandelt. Wir haben gesehen, was der Zweck war und wie man es benutzt. Wir haben auch einige Alternativen zu seiner Verwendung gelernt.

Den vollständigen Code dieses Artikels finden Sie wie gewohnt auf unserem GitHub.