Java 7 ile birlikte derleyicimiz (
javac) daha akıllı ve bize üretkenliğimizi arttıracak kolaylıklar sunuyor. Örnek olarak aşağıdaki gibi bir kodu ele alalım:
Map<String,Function<List<String>,String>> functionLookup =
new HashMap<String,Function<List<String>,String>>();
functionLookup bir yerel değişken ve tipi
Map<String,Function<List<String>,String>>
olarak tanımlanmış. Yerel bir değişkeni kullanmadan önce mutlaka ilklendirmelisiniz. Yukarıda bunu new
HashMap<String,Function<List<String>,String>>()
ataması ile gerçekleştiriyoruz. Ancak kodda soysal (=generic) parametrelerin bolca tekrarı var.
Java 7 bu tekrarın önüne geçiyor:
Map<String,Function<List<String>,String>> functionLookup = new HashMap<>();
Java 10 ile gelen yeniliklerden sadece bir tanesi dil ile ilgili:
var.
var yeni bir anahtar kelime değil, yeni bir tip tanımlıyor. Dolayısı ile
var kelimesini bir değişken ismi olarak kullanabilirsiniz!
var tipini yerel değişkenleri tanımlarken kullanıyoruz:
Ama siz yine de
var ismini değişken ismi olarak kullanmayın!
Java 10 ile birlikte yerel değişkene atama yaparak tanımlıyorsanız değişken isminin sol tarafında tipini söylemenize gerek yok. Derleyecimiz az bir çabayla değişkene yaptığınız atamada kullanılan değerin tipinden otomatik olarak tip bilgisini çıkarabiliyor.
Bunu sadece yerel değişkenler için yapabilirsiniz ve
mutlaka atama yapmanız gerekir. Yukarıdaki basit örnekte
var değişkeninin tipi
int olacaktır.
Şimdi aşağıdaki gibi kodu inceleyelim:
public class Exercise {
public static void main(String[] args) {
Comparator<String> orderByStringLengthDesc = (x, y) -> y.length() - x.length();
Comparator<String> orderByStringLengthAsc = orderByStringLengthDesc.reversed();
Function<List<String>, Optional<String>> longest = list -> list.stream().sorted(orderByStringLengthDesc)
.findFirst();
Function<List<String>, Optional<String>> shortest = list -> list.stream().sorted(orderByStringLengthAsc)
.findFirst();
List<Function<List<String>, Optional<String>>> funs = List.of(longest, shortest);
final List<String> names = List.of("Jack Shephard", "Kate Austen", "Ben Linus", "James Ford", "Hugo Reyes",
"John Locke", "Sayid Jarrah", "Richard Alpert", "Desmond Hume", "Daniel Faraday", "Shannon Rutherford",
"Danielle Rousseau");
List<String> processed = funs.stream().map(fr -> fr.apply(names)).filter(Optional::isPresent).map(Optional::get)
.collect(Collectors.toList());
processed.forEach(System.out::println);
}
}
Bu kodda tip tanımının çokça tekrar edilerek yapıldığı yer bulunuyor. Yukarıdaki kodu
var kullanarak herhangi bir belirsizliğe yok açmadan yeniden tanımlayabiliriz:
public class SmartCompiler {
public static void main(String[] args) {
Comparator<String> orderByStringLengthDesc = (x, y) -> y.length() - x.length();
var orderByStringLengthAsc = orderByStringLengthDesc.reversed();
Function<List<String>, Optional<String>> longest = list -> list.stream().sorted(orderByStringLengthDesc)
.findFirst();
Function<List<String>, Optional<String>> shortest = list -> list.stream().sorted(orderByStringLengthAsc)
.findFirst();
var funs = List.of(longest, shortest);
final var names = List.of("Jack Shephard", "Kate Austen", "Ben Linus", "James Ford", "Hugo Reyes", "John Locke",
"Sayid Jarrah", "Richard Alpert", "Desmond Hume", "Daniel Faraday", "Shannon Rutherford",
"Danielle Rousseau");
var processed = funs.stream().map(fr -> fr.apply(names)).filter(Optional::isPresent).map(Optional::get)
.collect(Collectors.toList());
processed.forEach(System.out::println);
}
}
Evet,
var kullanımı tip tanımlarken yapılan tekrarlardan kurtulmamızı sağladı. Burada var kullanımı ile ilgili dikkat edilmesi gereken birkaç nokta bulunuyor:
var ile
Lambda ifadesi yazamazsınız. Değişkenin tipini mulaka bir
@FunctionalInterface ile ilişkilendirmelisiniz. Yukarıdaki örnekte
orderByStringLengthDesc değişkenini
var orderByStringLengthDesc = (x, y) -> y.length() - x.length() biçiminde tanımlayamazsınız. Derleyici lambda ifadesinin hangi
@FunctionalInterface ile ilişkili olduğunu çıkarsayamaz! Derleyiciden imkansızı istemeyin! Ancak var orderByStringLengthAsc
= orderByStringLengthDesc
.reversed(); tanımlamasında bir sorun yok. Çünkü bu kez sağ taraftaki lambda ifadesinin hangi
@FunctionalInterface ile ilişkili olduğu belirli:
Comparator<String>.
var'ın kullanılabildiği yerleri özetleyen bir başka örnek daha inceleyelim:
@Override
public Optional<Country> findOne(String code) {
try (Connection connection = ds.getConnection();) {
PreparedStatement st = connection.prepareStatement(SELECT_COUNTRY_BY_CODE);
st.setString(1, code);
ResultSet rs = st.executeQuery();
if (rs.next()) {
Country country = new Country();
country.setCode(code);
country.setName(rs.getString(COLUMN_NAME));
country.setContinent(rs.getString(COLUMN_CONTINENT));
country.setPopulation(rs.getInt(COLUMN_POPULATION));
country.setSurfaceArea(rs.getDouble(COLUMN_SURFACE_AREA));
return Optional.of(country);
}
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
return Optional.empty();
}
var'ı blok içinde yerel değişken tanımlarken, Java 7'de
try değişkenini tanımlarken ve
for döngü değişkenini tanımlarken kullanabiliriz:
@Override
public Optional<Country> findOne(String code) {
try (var connection = ds.getConnection();) {
var st = connection.prepareStatement(SELECT_COUNTRY_BY_CODE);
st.setString(1, code);
var rs = st.executeQuery();
if (rs.next()) {
var country = new Country();
country.setCode(code);
country.setName(rs.getString(COLUMN_NAME));
country.setContinent(rs.getString(COLUMN_CONTINENT));
country.setPopulation(rs.getInt(COLUMN_POPULATION));
country.setSurfaceArea(rs.getDouble(COLUMN_SURFACE_AREA));
return Optional.of(country);
}
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
return Optional.empty();
}
Genel olarak kodun okunabilirliği için değişkenlere isim verirken özenli davranmak gerekir.
var ile tanımlanan değişkenler için ise bu pratiğe daha da sıkı sarılmak gerekir:
@Override
public Optional<Country> findOne(String code) {
try (var connection = ds.getConnection();) {
var prepStatement = connection.prepareStatement(SELECT_COUNTRY_BY_CODE);
prepStatement.setString(1, code);
var resultSet = prepStatement.executeQuery();
if (resultSet.next()) {
var country = new Country();
country.setCode(code);
country.setName(resultSet.getString(COLUMN_NAME));
country.setContinent(resultSet.getString(COLUMN_CONTINENT));
country.setPopulation(resultSet.getInt(COLUMN_POPULATION));
country.setSurfaceArea(resultSet.getDouble(COLUMN_SURFACE_AREA));
return Optional.of(country);
}
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
return Optional.empty();
}
Ancak
var kulanımında her tip çıkarsaması yukarıda verilen örneklerde olduğu kadar açık ve yalın olmayabilir:
var numbers = List.of(4,8,15,16,23);
Yukarıdaki örnekte
numbers değişkeninin tipi
List<Integer> olacaktır. Şimdi ufak bir değişiklik yapalım:
var numbers = List.of(4,8,15,16,23.);
Dikkatli bakılırsa
23 değerinin
23. olarak değiştirildiği görülebilir. Evet, bir nokta koymak
numbers değişkeninin tipini epey değiştirdi:
List<Number & Comparable<?>>. Şimdi biraz daha büyük bir değişiklik yapalım:
var numbers = List.of(4,8,15,16.,23,"forty two");
Bu durumda
numbers değişkeninin tipi
List<Object & Serializable & Comparable<?>> olacaktır.