JDK 10 ve “var” Özelliği

Malumunuzdur, artık Java’nın, Standard Edition (SE) dediğimiz çekirdek dilinin gelişimi, 2-3 senede bir olan big-bang sürümlerle değil, ufak-tefek ama yılda iki defa yapılacak sürümlerle olacak. Bu yeni durumun ilk uygulaması 20 Mart 2018’de çıkacak olan 10. sürümdür. Java SE 10’ın standart gerçekleştirmesi olan JDK 10’un Open JDK sayfasına buradan ulaşabilirsiniz. JDK 10’un aday sürümlerini de buradan indirebilirsiniz.

Java SE 10 ile gelen en temel dil özelliği “var” anahtar kelimesidir. “var” anahtar kelimesi, JEP 236 ile Java’ya katılmış yerel değişkenler (local variables) için tip çıkarımı” ( Local-Variable Type Inference) yapan bir yapıdır. Bu yapı ile yerel bir değişken tanımlarken (definition) kullanılan “tip değişkenAdı = ilkDeğer” formatında artık “tip” bilgisininin verilmesine gerek yoktur. Tip bilgisi yerine “var” ile yerel bir değişken tanıtılabilir. Dolayısıyla aşağıdaki tanımlar aynıdır:

int i = 5;
var i = 5;

Fakat şu satırın problemli olduğunu rahatlıkla farkedebilirsiniz:

var i;

Çünkü yukarıdaki durumda “i” sadece tanıtılmakta (declaration) ama tanılanmamaktadır çünkü bir ilk değer ataması yapılmamıştır. Bu durumda derleyici “i”nin tipini nereden belirleyecek? Bu yüzden derleyici “cannot infer type for local variable i” hatası verecektir.

Şimdi aşağıdaki daha geniş örneğe bakalım:

package org.javaturk.java10;

import java.util.*;
import java.util.stream.*;

public class VarExample{
	private static String[] stringIntArray = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
			"17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35",
			"36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50"};
	
    //  private var m = 10;            // Hata: 'var' is not allowed here
    //  private static var b = true;   // Hata: 'var' is not allowed here

	public static void main(String ... args){

		System.out.println("var Example");
		var k = 5;
		System.out.println("k: " + k);

		//i = true; // "var" sadece tip çıkarımı sağlar, dinamik tip yapısı değildir. 

		var d = new Date();
		System.out.println("Date: " + d);

		var list = new ArrayList<String>();
		list.add("Java 9");
		list.add("Java 10");
		System.out.println("List: " + list);

		var addition = new MathOperation(){
			@Override
			public double operate(double arg1, double arg2){
				return arg1 + arg2;
			}
		};

		System.out.println(Math.PI + " + " + Math.PI + ": " + addition.operate(Math.PI, Math.PI));

		MathOperation multiplication = (arg1, arg2) -> arg1 * arg2;
		System.out.println(Math.PI + " * " + Math.PI + ": " + multiplication.operate(Math.PI, Math.PI));
	
		Stream<String> stream = Arrays.stream(stringIntArray);
		var size = stream.map(s -> Integer.parseInt(s)).filter(i -> i % 2 == 0).map(i -> Math.sqrt(Math.sqrt(i))).map(i -> i * i * i * i).filter(i -> i > 5).count();
		System.out.println("Size: " + size);
	}

	interface MathOperation{
		public double operate(double arg1, double arg2);
	}
}

Burada bir kaç noktaya dikkat çekmek istiyorum. İlki, “var”ın sadece yerel değişkenler için kullanılabileceği kuralıdır. “var”, nesne (object ya da instance) ya da sınıf (class) değişkenleri için kullanılamaz, kullanılması halinde, “‘var’ is not allowed here” mesajlı bir derleme zamanı hatası alınır. Bir diğer konu ise “var”ın bize sadece daha az kod yazmamızı sağlayan basit bir yapı olduğu gerçeğidir. “var” hiç bir şekilde Java’nın statik tipli yapısını değiştirmez, dinamik tipli hale getirmez. Dolayısıyla yukarıdaki kod örneğinde “i = true;” geçerli bir kod değildir ve bundan dolayı da derleyici, “true”nun “int” tipine çevrilemediğini söyleyerek hata verecektir.

Programlama dillerinde “var”, “def” hatta “var” ile farklı bir anlamda kullanılan “val” gibi kısaltmalar ile değişken, sabite ya da metot tanımlarken kolaylık sağlanması özellikle modern dönemde sanki bir standart haline gelmiş durumda. Kotlin ve Go dilleri en taze örneklerden. Java’ya geç de olsa böyle bir yapının gelmesi, kod yazarken üç-beş tuş vurmaktan daha fazla kolaylık sağlayacak. Örneğin yukarıdaki kodda stream nesnesini işleyerek bir long üreten satırı düşünün. Bu gibi satırlarda dönen nesnenin ne olduğuna karar vermek bazen zaman alabilir. Yukarıdaki örnekte satırdaki en son metot çağrısı olan count()‘ın muhtemelen bir int döndürdüğünü düşünürüz ama gerçekte dönen bir long‘tur. Bu türden ufak-tefek can sıkıcı ve hız kesen durumlar, kod yazarken hemen her dakika karşılaştığımız cinstendir. Aşağıdaki gibi bir kod parçasında dönüş tipi daha da karmaşıklaştığında “var” kullanımı ciddi kolaylık sağlayabilir.

“var”, sadece yerel değişken tanıtırken ya da tanımlarken kullanılabileceğinden, for ya da while gibi çevrimlerde yer alabilir:

 
    var sum = 0;
	for(var i = 0; i < 20; i++){
		var remainder = i % 2;
		if(remainder == 0)
			sum+= i;
	}

Ama metot arayüzlerinde yer alamaz, aşağıdaki iki kullanım için de derleyiciden “‘var’ is not allowed here” hatası alırsınız.

     public var returnSomething(){
		return 5;
	}

	public void receiveSomething(var arg){
		
	}

 

Ama metottan dönen değeri atarjen “var” kullanabiliriz:

    
public static void main(String ... args){
		var i = returnSomething();
		
	}

    public static int returnSomething(){
		return 5;
	}

 

“var”ı dizilerde de kullanmak aşağıdaki gibi küme parantezi içinde ilk değer verildiği hallerde problemlidir:

  
   var intArray = {1, 2, 3};

Bu durumda derleyici “(array initializer needs an explicit target-type)” mesajıyla hata verir. Siz zekailik yapıp acaba tip bilgisini örneğin “L” ile versem ne olur derseniz:

   var longArray = {1L, 2L, 3L};

kodunuzun akibeti yine aynı olacaktır.

Fakat aşağıdaki gibi kullanımda hem “intArray” tanımlamada hem de dizideki elemanları for çevriminde almada problem yoktur:

  
   var intArray = new int[3];
	intArray[0] = 1;
	intArray[1] = 2;
	intArray[2] = 3;
	for(var t : intArray)
		System.out.println(t);

 

Bu konuda söyleyebileceğimiz son şey ise lambda ifadelerini ya da metot referanslarını atamada yine “var” kullanımının mümkün olmadığıdır. Aşağıdaki örneklerden de farkedeceğiniz üzere bu ifadelerde tip bilgisi, atamanın sağ tarafından çıkmaz. Örneğin “division” tanımında kastedilen aslen onun MathOperation tipinde bir referans değişkeni olduğudur ama bu bilgi “var” kullanımında kaybolmaktadır. Bu yüzden derleyici hep “(lambda expression needs an explicit target-type)” ya da “(method reference needs an explicit target-type)” hatası verecektir.

  
   var division = (arg1, arg2) -> arg1 / arg2;
   var printLine = System.out::println;

 

Bu sebeple yukarıdaki iki satırın doğru halleri şöyle olmalıdır:

  
   MathOperation division = (arg1, arg2) -> arg1 / arg2;	
   Consumer printLine = System.out::println;

 

Java 10 ve en temel dil yeniliği olan “var” kullanımıyla ilgili bu kadar ile iktifa edelim.
Bol “var”lı günler 🙂

Toplam görüntülenme sayısı: 3571