Void Type in Java

Overview

Java fejlesztőként talán már találkoztunk a Void típussal, és elgondolkodtunk azon, hogy mi a célja.

Ebben a gyors bemutatóban megismerjük ezt a különös osztályt, és megnézzük, mikor és hogyan használjuk, valamint hogyan kerüljük el a használatát, ha lehetséges.

Mi a Void típus

A JDK 1.1 óta a Java biztosítja számunkra a Void típust. Célja egyszerűen az, hogy a void visszatérési típust osztályként ábrázolja, és tartalmazzon egy Class<Void> nyilvános értéket. Nem instanciálható, mivel egyetlen konstruktora privát.

Ezért az egyetlen érték, amit egy Void változóhoz rendelhetünk, az a null. Kicsit haszontalannak tűnhet, de most megnézzük, mikor és hogyan használhatjuk ezt a típust.

Használtatások

Van néhány helyzet, amikor a Void típus használata érdekes lehet.

3.1. A Void típus használata. Tükrözés

Először is használhatjuk, amikor tükrözést végzünk. Valóban, bármely void metódus visszatérési típusa megegyezik a Void.TYPE változóval, amely a korábban említett Class<Void> értéket tartalmazza.

Lássunk el egy egyszerű Calculator osztályt:

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

Egyik metódus egész számot ad vissza, másik nem ad vissza semmit. Most tegyük fel, hogy reflexióval le kell kérdeznünk az összes olyan metódust, amely nem ad vissza eredményt. Ezt a Void.TYPE változó segítségével érjük el:

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

Mint látjuk, csak a clear() és a print() metódusok kerültek lekérdezésre.

3.2. A Void.TYPE változót használjuk. Generikák

A Void típus másik felhasználása a generikus osztályoknál történik. Tegyük fel, hogy egy olyan metódust hívunk meg, amely Callable paramétert igényel:

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

De az átadni kívánt Callable-nek nem kell visszaadnia semmit. Ezért átadhatunk egy Callable<Void>:

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

Egy tetszőleges típust (pl. Callable<Integer>) is használhattunk volna, és nullát vagy egyáltalán semmilyen típust (Callable) adhattunk volna vissza, de a Void használata egyértelműen kifejezi a szándékunkat.

Ezt a módszert lambdákra is alkalmazhatjuk. Ami azt illeti, a Callable-ünket akár lambdaként is megírhattuk volna. Képzeljünk el egy Function-t igénylő metódust, de mi egy olyan Function-t szeretnénk használni, amely nem ad vissza semmit. Akkor csak azt kell elérnünk, hogy Voidot adjon vissza:

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

Hogyan kerüljük el a használatát?

Most, láttuk a Void típus néhány felhasználási módját. Azonban még ha az első használat teljesen rendben is van, akkor is érdemes elkerülni a Void használatát a generikusokban, ha lehetséges. Valóban, egy olyan visszatérési típussal való találkozás, amely az eredmény hiányát reprezentálja, és csak nullot tartalmazhat, nehézkes lehet.

Most megnézzük, hogyan kerülhetjük el ezeket a helyzeteket. Először is nézzük meg a Callable paraméterrel rendelkező metódusunkat. Hogy elkerüljük a Callable<Void> használatát, felajánlhatunk helyette egy másik metódust, amely Runnable paramétert vesz fel:

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

Ezért átadhatunk egy Runnable-t, amely nem ad vissza értéket, és így megszabadulhatunk a haszontalan return null-tól:

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

De akkor mi van, ha a Defer osztály nem a miénk, amit módosíthatunk? Akkor vagy maradunk a Callable<Void> lehetőségnél, vagy létrehozunk egy másik osztályt, amely egy Runnable-t vesz fel, és a hívást a Defer osztályra halasztja:

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

Ezzel egyszer és mindenkorra a saját módszerünkbe kapszulázzuk a nehézkes részt, így a jövőbeli fejlesztők egyszerűbb API-t használhatnak.

A Function esetében természetesen ugyanezt elérhetjük. Példánkban a Function nem ad vissza semmit, így helyette biztosíthatunk egy másik metódust, amely egy Consumer-t vesz fel:

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

Mi van akkor, ha a függvényünk nem vesz fel semmilyen paramétert? Vagy használhatunk egy Runnable-t, vagy létrehozhatunk egy saját funkcionális interfészt (ha ez egyértelműbbnek tűnik):

public interface Action { void execute();}

Ezután ismét túlterheljük a defer() metódust:

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

Következtetés

Ebben a rövid cikkben a Java Void osztályt ismertettük. Láttuk, mi volt a célja és hogyan használjuk. Megismertünk néhány alternatívát is a használatára.

A cikk teljes kódja szokás szerint megtalálható a GitHub oldalunkon.

Kezdje el a Spring 5 és Spring Boot 2 használatát a Tanulj Spring tanfolyamon keresztül:

>> KATTINTSON A TANFOLYAMRA

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.