Tip Void în Java

Vizualizare

Ca dezvoltatori Java, este posibil să fi întâlnit tipul Void la un moment dat și să ne fi întrebat care este scopul său.

În acest tutorial rapid, vom învăța despre această clasă ciudată și vom vedea când și cum să o folosim, precum și cum să o evităm atunci când este posibil.

Ce este tipul Void

De la JDK 1.1, Java ne pune la dispoziție tipul Void. Scopul său este pur și simplu de a reprezenta tipul de returnare void ca o clasă și de a conține o valoare publică Class<Void>. Nu este instanțiabil, deoarece singurul său constructor este privat.

Din acest motiv, singura valoare pe care o putem atribui unei variabile Void este null. Poate părea un pic inutil, dar vom vedea acum când și cum să folosim acest tip.

Utilizări

Există câteva situații în care utilizarea tipului Void poate fi interesantă.

3.1. Reflecție

În primul rând, am putea să-l folosim atunci când facem reflecție. Într-adevăr, tipul de retur al oricărei metode void se va potrivi cu variabila Void.TYPE care deține valoarea Class<Void> menționată mai devreme.

Să ne imaginăm o clasă simplă Calculator:

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

Câteva metode returnează un număr întreg, altele nu returnează nimic. Acum, să presupunem că trebuie să recuperăm, prin reflexie, toate metodele care nu returnează niciun rezultat. Vom realiza acest lucru folosind variabila Void.TYPE:

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

După cum putem vedea, au fost recuperate doar metodele clear() și print().

3.2. Generice

O altă utilizare a tipului Void este în cazul claselor generice. Să presupunem că apelăm o metodă care necesită un parametru Callable:

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

Dar, Callable-ul pe care vrem să-l trecem nu trebuie să returneze nimic. Prin urmare, putem trece un 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();}

Am fi putut fie să folosim un tip aleatoriu (de exemplu, Callable<Integer>) și să returnăm null, fie să nu returnăm niciun tip (Callable), dar folosirea lui Void afirmă clar intențiile noastre.

Am putea aplica această metodă și la lambdas. De fapt, Callable-ul nostru ar fi putut fi scris ca un lambda. Să ne imaginăm o metodă care necesită o Funcție, dar dorim să folosim o Funcție care nu returnează nimic. Atunci trebuie doar să o facem să returneze Void:

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

Cum să evităm să o folosim?

Acum, am văzut câteva utilizări ale tipului Void. Cu toate acestea, chiar dacă prima utilizare este în totalitate în regulă, am putea dori să evităm, dacă este posibil, să folosim Void în generice. Într-adevăr, întâlnirea cu un tip return care reprezintă absența unui rezultat și care nu poate conține decât null poate fi incomodă.

Vom vedea acum cum să evităm aceste situații. Mai întâi, să luăm în considerare metoda noastră cu parametrul Callable. Pentru a evita utilizarea unui Callable<Void>, am putea oferi o altă metodă care să ia în schimb un parametru Runnable:

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

Așa, îi putem trece un Runnable care nu returnează nicio valoare și astfel să scăpăm de inutilul return null:

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

Dar atunci, ce se întâmplă dacă clasa Defer nu este a noastră pentru a o modifica? Atunci putem fie să rămânem la opțiunea Callable<Void>, fie să creăm o altă clasă care să ia un Runnable și să amâne apelul către clasa Defer:

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

Făcând acest lucru, încapsulăm o dată pentru totdeauna partea incomodă în propria noastră metodă, permițând viitorilor dezvoltatori să folosească un API mai simplu.

Desigur, același lucru poate fi realizat și pentru Function. În exemplul nostru, Funcția nu returnează nimic, astfel că putem furniza o altă metodă care să ia în schimb un Consumator:

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

Atunci, ce se întâmplă dacă funcția noastră nu ia niciun parametru? Putem folosi un Runnable sau putem crea propria noastră interfață funcțională (dacă pare mai clar):

public interface Action { void execute();}

Apoi, supraîncărcăm din nou metoda defer():

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

Concluzie

În acest scurt articol, am acoperit clasa Java Void. Am văzut care este scopul său și cum să o folosim. Am învățat, de asemenea, câteva alternative la utilizarea sa.

Ca de obicei, codul complet al acestui articol poate fi găsit pe GitHub.

Începeți să lucrați cu Spring 5 și Spring Boot 2, prin intermediul cursului Învățați Spring:

>>VEZI CURSUL

.

Lasă un răspuns

Adresa ta de email nu va fi publicată.