Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Java: Local-Variable Type Inference

Analizziamo la Local-Variable Type Inference (inferenza automatica del tipo) di Java, una caratteristica che non attiene direttamente il linguaggio ma la fase di compilazione del codice.
Analizziamo la Local-Variable Type Inference (inferenza automatica del tipo) di Java, una caratteristica che non attiene direttamente il linguaggio ma la fase di compilazione del codice.
Link copiato negli appunti

Una nuova caratteristica introdotta di recente in Java riguarda la riduzione della verbosità del codice. All'interno del codice di programmazione possiamo adesso utilizzare var, che non rappresenta una parola chiave ma un nome riservato di tipo, per la dichiarazione compatta di variabili locali.

Alla base del funzionamento del nome di tipo var è presente l'inferenza automatica del tipo. L'inferenza del tipo non è una caratteristica del linguaggio di programmazione ma soltanto delle capacità del compilatore.

Il funzionamento alla base della deduzione automatica del tipo è molto semplice: il compilatore non fa altro che analizzare il lato destro di un'istruzione var deducendo il tipo dichiarato in base a ciò che restituisce il lato destro dell'istruzione aggiungendolo quindi al bytecode finale. Vediamo un esempio. Nella seguente dichiarazione di un numero intero:

public static void main(String[] args) {
	var number = 10;
}

Il compilatore rileva il tipo int nella parte destra dell'istruzione inferendo quindi il tipo dichiarato come int. L'istruzione nel bytecode finale sarà:

int number = 10;

Abbiamo precedentemente fatto notare come var rappresenti un nome riservato di tipo. Questa caratteristica consente di utilizzare var come nome di metodo, identificatore di variabile, o nome di package. Ad esempio la seguenti istruzioni sono perfettamente lecite:

var var = 5;
public void var() {}
package var;

mentre non lo sono:

class var{}
interface var{}

var non può quindi essere utilizzata come nome di classe o interfaccia. L'inferenza locale del tipo può essere utilizzata nei seguenti casi:

  • Inizializzazione di variabili locali.
  • Indice nei cicli enhanced for.
  • Dichiarazione locale di variabile nei cicli for.

Esempi dei precedenti casi sono:

var list = new ArrayList<String>();
for (var element : list) {
	System.out.println(element);
}
for (var i = 0; i < list.size(); i++) {
	System.out.println(list.get(i));
}

La local type inference presenta anche delle limitazioni. Non è possibile utilizzare var senza inizializzazione, il seguente codice:

public static void main(String[] args) {
	var number;
}

produrrà l'errore:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
	Cannot use 'var' on variable without initializer

Oppure utilizzare l'inizializzazione a null:

public static void main(String[] args) {
	var number = null;
}

porterà all'errore:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
	Cannot infer type for local variable initialized to 'null'.

Non è possibile utilizzare l'operatore var per una dichiarazione multipla di variabili:

public static void main(String[] args) {
	var number1 = 10, number2 = 11;
}

con il seguente errore del compilatore:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
	'var' is not allowed in a compound declaration.

Non è inoltre consentito utilizzare var nella dichiarazione di array che prevedano
l'utilizzo extra di parentesi quadre:

public static void main(String[] args) {
	var integers[] = new Integer[10];
}

error mostrato:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
	'var' is not allowed as an element type of an array.

Infine var non può essere utilizzata in espressioni lambda, riferimenti a metodi, ed inizializzazione di array. Alcuni esempi ed errori di compilazione:

public static void main(String[] args) {
	var max = (v1, v2) -> v1 >= v2 ? v1 : v2;
}

Errore mostrato:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
	Lambda expression needs an explicit target-type.

public static void main(String[] args) {
	var max = Math::max;
}

Con l'errore di compilazione:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
	Method reference needs an explicit target-type.

public static void main(String[] args) {
	var values = {'a','b','c','d','e'};
}

Avendo l'errore di compilazione:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
	Array initializer needs an explicit target-type.

L'introduzione di var potrebbe far pensare ad una evoluzione del linguaggio Java verso una forma dinamica del tipo ma in realtà non è cosi, come detto in precedenza
var è soltanto una caratteristica della fase di compilazione, il bytecode generato rimane esattamente lo stesso che si avrebbe senza l'utilizzo di var.

Java rimane quindi un linguaggio tipizzato statico. L'erasure è alla base del funzionamento dei Generics in Java occupandosi, durante la fase di compilazione, della risoluzione dei parametri specificati nelle classi o nei metodi, con il tipo specifico. La local type inference è possibile anche con espressioni Generics.

I seguenti esempi:

var list1 = new ArrayList<String>();
var list2 = new ArrayList<>();
var list3 = new ArrayList();

Portano alla deduzione, da parte del compilatore, rispettivamente di un tipo ArrayList<String>, di un tipo ArrayList<Object> e di un semplice ArrayList. L'utilizzo di var è consentito anche con le classi anonime:

var runnable = new Runnable() {
	@Override
	public void run() {
	}
};

Un'espressione il cui tipo non è deducibile è conosciuta come Non Denotable Type. Un'aspetto molto interessente che Java offre è la possibilità di realizzare casi speciali di tipi Non Denotable consentendo la realizzazione di stutture dinamiche per un contesto temporaneo.

Un esempio molto semplice: immaginiamo di avere una classe Calcolo che offre un solo metodo per il calcolo del minimo tra due numeri interi. Supponiamo adesso che all'interno di un particolare punto di codice necessitiamo del calcolo del massimo tra due numeri. Possiamo evitare di modificare la classe esistente (cosa che potrebbe anche non essere possibile) realizzando
un tipo Non Denotable a partire dalla classe Calcolo:

public class Main {
	static class Calcolo {
		public int min(int a, int b) {
			return a<=b?a:b;
		}
	}
	public static void main(String[] args) {
		var extraCalcolo = new Calcolo() {
			public int max(int a, int b) {
				return a>=b?a:b;
			}
		};
		System.out.println(extraCalcolo.max(3, 4));
		System.out.println(extraCalcolo.min(3, 4));
	}
}

L'identificativo extraCalcolo è un tipo sconosciuto che però consente l'utilizzo del metodo min() della classe Calcolo e il nuovo metodo max() definito mediante classe anonima.

Ti consigliamo anche