Java 8 – Lambda, Stream API ve Daha Fazlası

Java 8 – Lambda, Stream API ve Daha Fazlası

Yepyeni bir seriye başlıyoruz. Java versiyonları hakkında bilgi vermeye çalışacağız. Yeni versiyonlar yayınlandıkça serimizi güncelleyeceğiz. Yeni versiyonlar yayınlandığında birçok konuda yenilikler gelmektedir ancak en temel anlamda geliştiriceleri etkileyen konuları anlatmaya, örnek kod parçaları ile pekiştirmeye çalışacağız. Keyifli okumalar dilerim 🙂

Tabi javanın ilk versiyonlarını da ileriki bir dönemde anlatmaya çalışırım ama en çok kullanılan ve Java versiyonlarının başı gibi görebileceğimiz Java 8 ile başlayacağız. Java 8 sektörde en çok kullanılan versiyonlardan biridir.

Kısaca nedir bu JAVA ?

Java, 1995 yılında Sun Microsystems tarafından yayınlanmış bir hesaplama platformu ve programlama dilidir. Platform bağımsız, nesne yönelimli, yüksek performanslı, açık kaynak kodlu, yüksek seviyeli bir dildir. İlk versiyon 1996 yılında yayınlanmıştır. Java 8 2014 yılında yayınlanmıştır.

Java 8 ile Gelen Özellikler

Lambda Expressions

Herhangi bir class’a ait olmayan fonksiyon yazabilmeyi sağlamaktadır. Bu bağlamda functional programing dünyasına girmiş bulunuyoruz. Oluşturduğumuz fonksiyonu tekrar tekrar kullanabiliriz veya farklı yerlere parametre olarak gönderebiliriz.

Syntax yapısı;
(argument-list) -> {body}  

// 2 Parametre alan lambda örneği

interface Addable{  
    int add(int a,int b);  
}  
  
public class LambdaExpressionExample5{  
    public static void main(String[] args) {  
              
        Addable ad1=(a,b)->(a+b);  
        System.out.println(ad1.add(10,20));  
          
        Addable ad2=(int a,int b)->(a+b);  
        System.out.println(ad2.add(100,200));  
    }  
}  

// Liste içinde kullanım örneği

public class LambdaExpressionExample7{  
    public static void main(String[] args) {  
          
        List<String> list=new ArrayList<String>();  
        list.add("ankit");  
        list.add("mayank");  
        list.add("irfan");  
        list.add("jai");  
          
        list.forEach((n)->System.out.println(n)  
        );  
    }  
}  

Functional Interfaces

Sadece bir tane abstract methodu bulunan interfacelerdir. Lambda ifadelerle entegre çalışmaktadır. İnterface içerisinde default ve static methodlar yazabilmemize imkan verir.

@FunctionalInterface anatasyonu ile bu interface’in functional interface olduğunu belirtiyoruz. İçinde sadece add() adında bir tane abstrat method var. Lambda ifadesi ile methodun body tarafını yazmış oluyoruz.
Static methodları ve default methodları direk çağırıp kullanabiliriz ama bunun yanında override ederek farklı davranmasınıda sağlayabiliriz.

// Functional Interface Örneği

public class App {
    public static void main(String[] args) {
        Addable addNumber = (s1, s2) -> System.out.println(s1 + s2);
        addNumber.add(2, 5);
    }
}

@FunctionalInterface
interface Addable {
    void add(int s1, int s2);
}
// Default method ve static method Örneği

public class App {
    public static void main(String[] args) {
        Addable.staticWriter();
        Addable addNumber = (s1, s2) -> System.out.println(s1 + s2);
        addNumber.add(2, 5);
        addNumber.defaultWriter();
    }
}

@FunctionalInterface
interface Addable {
    void add(int s1, int s2);
 
    static void staticWriter() {
       System.out.println("Toplamaya başlıyorum.");
    }

    default void defaultWriter() {
       System.out.println("Topladım");
    }
}

Method References

Methodları bir nesne gibi davranmasını sağlayarak başka methodlara parametre olarak gönderebilmemize olanak tanır. Static classlarda class name ile static olmayan classlarda instance ile erişebiliyoruz. “::” söz dizimini kullanırız.

class StringUtil {
    String toUpperCase(String str) {
        return str.toUpperCase();
    }
}

public class ObjectMethodReferenceExample {
    public static void main(String[] args) {
        StringUtil stringUtil = new StringUtil();
        
        // Lambda expression
        Function<String, String> lambdaFunction = (str) -> stringUtil.toUpperCase(str);
        System.out.println(lambdaFunction.apply("hello"));
        
        // Method reference
        Function<String, String> referenceFunction = stringUtil::toUpperCase;
        System.out.println(referenceFunction.apply("world"));
    }
}

Stream API

Collectionlar üzerinde sık kullanılan işlemlerin kolaylıkla uygulanmasını sağlamaktadır. Bunlardan bazıları aşağıdaki gibidir.

  • filter (filtreleme)
  • forEach (itere etme)
  • map (dönüştürme)
  • reduce (indirgeme)
  • distinct (tekil hale getirme)
  • limit (limitleme)
  • collect (toplama)
  • count (sayma)
  • min / max  (sıralama ile max-min eleman bulma)
public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Emma");
        
        // Stream kullanarak listeyi filtreleme ve yeni bir liste oluşturma
        List<String> filteredNames = names.stream()
                                          .filter(name -> name.startsWith("A"))
                                          .collect(Collectors.toList());
        System.out.println("Filtered names starting with 'A': " + filteredNames);
        
        // Stream kullanarak listeyi harf sırasına göre sıralama
        List<String> sortedNames = names.stream()
                                        .sorted()
                                        .collect(Collectors.toList());
        System.out.println("Sorted names: " + sortedNames);
        
        // Stream kullanarak listedeki her elemanın uzunluğunu hesaplama
        List<Integer> nameLengths = names.stream()
                                         .map(String::length)
                                         .collect(Collectors.toList());
        System.out.println("Length of names: " + nameLengths);
        
        // Stream kullanarak listedeki elemanları birleştirme
        String concatenatedNames = names.stream()
                                        .reduce("", (partialResult, name) -> partialResult + "-" + name);
        System.out.println("Concatenated names: " + concatenatedNames);
    }
}

Optional Class

Nesneleri kullanmadan önce NullCheck kontrolünü daha okunabilir ve kontrol edilebilir bir şekilde kullanmamızı sağlar. Optional<T> yapısını kullanarak eğer obje null değilse bu şekilde davran null ise farklı şekilde davranmasını hatta istersek hata fırlatmasını sağlayabiliriz.

public class Main {
    public static void main(String[] args) {
        // Bir değere sahip olan bir Optional oluşturma
        Optional<String> optionalValue = Optional.of("Merhaba");

        // Bir değere sahip olmayan bir Optional oluşturma
        Optional<String> emptyOptional = Optional.empty();

        // Bir değere sahip olabilecek bir Optional oluşturma
        Optional<String> nullableOptional = Optional.ofNullable(null);

        // Bir Optional nesnesinin değerini almak
        String value = optionalValue.get();
        System.out.println("optionalValue değeri: " + value);

        // Bir Optional nesnesinin değerini kontrol etmek ve varsa bir işlem yapmak
        optionalValue.ifPresent(val -> System.out.println("optionalValue değeri var: " + val));

        // Bir Optional nesnesinin değerini kontrol etmek ve varsa bir işlem yapmak, yoksa varsayılan bir değer döndürmek
        String orElseValue = emptyOptional.orElse("Varsayılan Değer");
        System.out.println("emptyOptional değeri veya varsayılan değer: " + orElseValue);

        // Bir Optional nesnesinin değerini kontrol etmek ve varsa bir işlem yapmak, yoksa bir exception fırlatmak
        try {
            String orElseThrowValue = emptyOptional.orElseThrow(() -> new IllegalStateException("Değer yok"));
            System.out.println("emptyOptional değeri veya istisna: " + orElseThrowValue);
        } catch (IllegalStateException e) {
            System.out.println(e.getMessage());
        }
    }
}

Date-Time API

Daha önceden date işlemler için Date sınıfı kullanılıyordu. Bu date sınıfı timezone özelliğine sahip değildi. Java 8 ile birlikte daha kapsamlı sınıf ve metotlar sağlayan java.time paketi eklenmiştir.

public class DateTimeExample {
    public static void main(String[] args) {
        // Bugünün tarihini al
        LocalDate bugun = LocalDate.now();
        System.out.println("Bugün: " + bugun);

        // Şu anki zamanı al
        LocalTime suAn = LocalTime.now();
        System.out.println("Şu an: " + suAn);

        // Şu anki tarih ve zamanı al
        LocalDateTime suAnDateTime = LocalDateTime.now();
        System.out.println("Şu anki Tarih ve Zaman: " + suAnDateTime);

        
        LocalDate ozelTarih = LocalDate.of(2024, 6, 12);

        // İki tarih arasındaki farkı hesapla
        Period fark = Period.between(bugun, ozelTarih);
        System.out.println("Bugün ile Özel Tarih arasındaki fark: " + fark.getYears() + " yıl, " + fark.getMonths() + " ay, " +                    fark.getDays() + " gün");

    }
}

👉 Sıradaki yazımızda Java 9 ile gelen Modüler Sistem ve diğer yeniliklere göz atacağız:
➡️ Java 9 – Modüler Sistem, REPL ve Yeni API’ler


📝 Kaynaklar:

Codest Blog

Codest Blog yazılım hakkında her konuda bilgi sağlayan bir blog sitesidir. Bilgi paylaştıkça güzeldir felsefesi ile hareket ederek, yazılımcıların en cok karşılaştığı konuları sizlere aktarmayı hedefliyoruz. Keyifli okumalar 🙂