1. Arayüzler

Lambda fonksiyonlarının ne olduğunu anlamak için öncelikle arayüzlerin ne olduğunu anlamanız gerekir. Öyleyse, ana noktaları hatırlayalım.

Arayüz , sınıf kavramının bir varyasyonudur. Büyük ölçüde kısaltılmış bir sınıf diyelim. Bir sınıfın aksine, bir arayüzün kendi değişkenleri olamaz (statik olanlar hariç). Ayrıca türü arabirim olan nesneler de oluşturamazsınız:

  • Sınıfın değişkenlerini bildiremezsiniz
  • nesne oluşturamazsın

Örnek:

interface Runnable
{
   void run();
}
Standart arayüz örneği

Arayüz kullanma

Peki bir arayüze neden ihtiyaç duyulur? Arabirimler yalnızca kalıtımla birlikte kullanılır. Aynı arabirim farklı sınıflar tarafından miras alınabilir veya daha önce de belirtildiği gibi — sınıflar arabirimi uygular .

Bir sınıf bir arabirim uygularsa, arabirim tarafından bildirilen ancak uygulanmayan yöntemleri uygulaması gerekir. Örnek:

interface Runnable
{
   void run();
}

class Timer implements Runnable
{
   void run()
   {
      System.out.println(LocalTime.now());
   }
}

class Calendar implements Runnable
{
   void run()
   {
      var date = LocalDate.now();
      System.out.println("Today: " + date.getDayOfWeek());
   }
}

Sınıf arabirimi Timeruygular Runnable, bu nedenle arabirimdeki tüm yöntemleri kendi içinde bildirmeli Runnableve bunları uygulamalıdır, yani bir yöntem gövdesinde kod yazmalıdır. Aynı şey Calendarsınıf için de geçerli.

Ancak artık Runnabledeğişkenler, arayüzü uygulayan nesnelere referansları saklayabilir Runnable.

Örnek:

kod Not
Timer timer = new Timer();
timer.run();

Runnable r1 = new Timer();
r1.run();

Runnable r2 = new Calendar();
r2.run();

run()Sınıftaki yöntem Timerçağrılacak Sınıftaki


yöntem çağrılacak Sınıftaki yöntem çağrılacak run()Timer


run()Calendar

Bu tür, nesnenin ata sınıflarından biri olduğu sürece, herhangi bir türdeki değişkene her zaman bir nesne başvurusu atayabilirsiniz. Timerve sınıfları için Calendarbu türden iki tür vardır: Objectve Runnable.

Bir değişkene bir nesne başvurusu atarsanız Object, yalnızca sınıfta bildirilen yöntemleri çağırabilirsiniz Object. Ve bir değişkene bir nesne başvurusu atarsanız Runnable, türün yöntemlerini çağırabilirsiniz Runnable.

Örnek 2:

ArrayList<Runnable> list = new ArrayList<Runnable>();
list.add (new Timer());
list.add (new Calendar());

for (Runnable element: list)
    element.run();

Bu kod çalışacaktır, çünkü Timerve Calendarnesneleri mükemmel bir şekilde çalışan çalıştırma yöntemlerine sahiptir. Yani, onları aramak sorun değil. Her iki sınıfa da bir run() yöntemi eklemiş olsaydık, onları bu kadar basit bir şekilde çağıramazdık.

Temel olarak, Runnablearabirim yalnızca run yöntemini koymak için bir yer olarak kullanılır.



2. Sıralama

Daha pratik bir şeye geçelim. Örneğin, dizeleri sıralamaya bakalım.

Bir dize koleksiyonunu alfabetik olarak sıralamak için Java'nın harika bir yöntemi vardır.Collections.sort(collection);

Bu statik yöntem, geçirilen koleksiyonu sıralar. Ve sıralama sürecinde, öğelerin değiş tokuşunun gerekip gerekmediğini anlamak için öğelerinin ikili karşılaştırmalarını gerçekleştirir.

compareToSıralama sırasında bu karşılaştırmalar, tüm standart sınıfların sahip olduğu () yöntemi kullanılarak gerçekleştirilir : Integer, String, ...

Integer sınıfının CompareTo() yöntemi iki sayının değerlerini karşılaştırırken, String sınıfının CompareTo() yöntemi dizelerin alfabetik sırasına bakar.

Böylece, bir sayı koleksiyonu artan düzende sıralanırken, bir dize koleksiyonu alfabetik olarak sıralanır.

Alternatif sıralama algoritmaları

Peki ya dizeleri alfabetik olarak değil de uzunluklarına göre sıralamak istiyorsak? Peki ya sayıları azalan düzende sıralamak istersek? Bu durumda ne yaparsın?

Bu tür durumlarla başa çıkmak için sınıfın iki parametresi olan Collectionsbaşka bir yöntemi vardır :sort()

Collections.sort(collection, comparator);

Karşılaştırıcı , bir sıralama işlemi sırasında bir koleksiyondaki nesnelerin nasıl karşılaştırılacağını bilen özel bir nesnedir . Comparator terimi , "karşılaştırmak" anlamına gelen Compare kelimesinden türeyen İngilizce comparator kelimesinden gelir.

Peki nedir bu özel nesne?

Comparatorarayüz

Her şey çok basit. sort()Yöntemin ikinci parametresinin türü :Comparator<T>

T, koleksiyondaki öğelerin türünü belirten bir tür parametresi ve tek bir yöntemi Comparatorolan bir arabirimdir.int compare(T obj1, T obj2);

Başka bir deyişle, bir karşılaştırma nesnesi, Karşılaştırıcı arabirimini uygulayan herhangi bir sınıfın herhangi bir nesnesidir. Karşılaştırıcı arayüzü çok basit görünüyor:

public interface Comparator<Type>
{
   public int compare(Type obj1, Type obj2);
}
Karşılaştırıcı arayüzü kodu

Yöntem compare(), kendisine iletilen iki bağımsız değişkeni karşılaştırır.

Yöntem negatif bir sayı döndürürse, bunun anlamı obj1 < obj2. Yöntem pozitif bir sayı döndürürse, bunun anlamı obj1 > obj2. Yöntem 0 döndürürse, bunun anlamı obj1 == obj2.

Dizeleri uzunluklarına göre karşılaştıran bir karşılaştırıcı nesne örneği:

public class StringLengthComparator implements Comparator<String>
{
   public int compare (String obj1, String obj2)
   {
      return obj1.length()obj2.length();
   }
}
StringLengthComparatorsınıfın kodu

Dize uzunluklarını karşılaştırmak için bir uzunluğu diğerinden çıkarmanız yeterlidir.

Dizeleri uzunluğa göre sıralayan bir programın tam kodu şöyle görünür:

public class Solution
{
   public static void main(String[] args)
   {
      ArrayList<String> list = new ArrayList<String>();
      Collections.addAll(list, "Hello", "how's", "life?");
      Collections.sort(list, new StringLengthComparator());
   }
}

class StringLengthComparator implements Comparator<String>
{
   public int compare (String obj1, String obj2)
   {
      return obj1.length()obj2.length();
   }
}
Dizeleri uzunluğa göre sıralama


3. sözdizimsel şeker

Ne dersiniz, bu kod daha derli toplu yazılabilir mi? Temel olarak, faydalı bilgiler içeren tek bir satır vardır — obj1.length() - obj2.length();.

Ancak kod bir yöntemin dışında var olamaz, bu yüzden bir compare()yöntem eklemek zorunda kaldık ve yöntemi saklamak için yeni bir sınıf eklemek zorunda kaldık — StringLengthComparator. Ayrıca değişkenlerin türlerini de belirtmemiz gerekiyor... Her şey doğru görünüyor.

Ancak bu kodu kısaltmanın yolları var. Sizin için biraz sözdizimsel şekerimiz var. İki kaşık toz!

Anonim iç sınıf

Karşılaştırma kodunu doğrudan yöntemin içine yazabilirsiniz main()ve derleyici gerisini halleder. Örnek:

public class Solution
{
    public static void main(String[] args)
    {
        ArrayList<String> list = new ArrayList<String>();
        Collections.addAll(list, "Hello", "how's", "life?");

        Comparator<String> comparator = new Comparator<String>()
        {
            public int compare (String obj1, String obj2)
            {
                return obj1.length()obj2.length();
            }
        };

        Collections.sort(list, comparator);
    }
}
Dizeleri uzunluğa göre sırala

ComparatorAçıkça bir sınıf oluşturmadan arabirimi uygulayan bir nesne oluşturabilirsiniz ! Derleyici otomatik olarak oluşturacak ve geçici bir isim verecektir. Hadi karşılaştıralım:

Comparator<String> comparator = new Comparator<String>()
{
    public int compare (String obj1, String obj2)
    {
        return obj1.length() – obj2.length();
    }
};
Anonim iç sınıf
Comparator<String> comparator = new StringLengthComparator();

class StringLengthComparator implements Comparator<String>
{
    public int compare (String obj1, String obj2)
    {
        return obj1.length() – obj2.length();
    }
}
StringLengthComparatorsınıf

İki farklı durumda aynı kod bloklarını belirtmek için aynı renk kullanılır. Pratikte farklar oldukça küçüktür.

Derleyici ilk kod bloğuyla karşılaştığında, karşılık gelen ikinci bir kod bloğu üretir ve sınıfa rastgele bir ad verir.


4. Java'da Lambda ifadeleri

Diyelim ki kodunuzda anonim bir iç sınıf kullanmaya karar verdiniz. Bu durumda, şöyle bir kod bloğunuz olacaktır:

Comparator<String> comparator = new Comparator<String>()
{
    public int compare (String obj1, String obj2)
    {
        return obj1.length() obj2.length();
    }
};
Anonim iç sınıf

Burada bir değişkenin bildirimini anonim bir sınıfın oluşturulmasıyla birleştiriyoruz. Ancak bu kodu kısaltmanın bir yolu var. Örneğin, bunun gibi:

Comparator<String> comparator = (String obj1, String obj2) ->
{
    return obj1.length()obj2.length();
};

Noktalı virgül gereklidir, çünkü burada yalnızca örtük bir sınıf bildirimimiz değil, aynı zamanda bir değişken yaratmamız da var.

Bunun gibi notasyona lambda ifadesi denir .

Derleyici, kodunuzda buna benzer bir gösterimle karşılaşırsa, yalnızca kodun ayrıntılı sürümünü (anonim bir iç sınıfla) oluşturur.

Lambda ifadesini yazarken sadece sınıfın adını değil , yöntemin adını da atladığımıza dikkat edin .Comparator<String>int compare()

Derleme yöntemi belirlemekte sorun yaşamaz çünkü bir lambda ifadesi yalnızca tek bir yöntemi olan arabirimler için yazılabilir . Bu arada, bu kuralı aşmanın bir yolu var, ancak bunu OOP'yi daha derinlemesine çalışmaya başladığınızda öğreneceksiniz (varsayılan yöntemlerden bahsediyoruz).

Kodun ayrıntılı versiyonuna tekrar bakalım, ancak bir lambda ifadesi yazarken atlanabilecek kısmı gri yapacağız:

Comparator<String> comparator = new Comparator<String>()
{
    public int compare (String obj1, String obj2)
   {
      return obj1.length()obj2.length();
   }
};
Anonim iç sınıf

Önemli bir şey atlanmamış gibi görünüyor. Aslında, Comparatorarayüzün yalnızca bir compare()yöntemi varsa, derleyici devre dışı kalan kodu kalan koddan tamamen kurtarabilir.

Sıralama

Bu arada sıralama kodunu şu şekilde yazabiliriz:

Comparator<String> comparator = (String obj1, String obj2) ->
{
   return obj1.length()obj2.length();
};
Collections.sort(list, comparator);

Hatta şöyle:

Collections.sort(list, (String obj1, String obj2) ->
   {
      return obj1.length()obj2.length();
   }
);

comparatorDeğişkeni hemen değişkene atanan değerle değiştirdik comparator.

Tür çıkarımı

Ama hepsi bu kadar değil. Bu örneklerdeki kod daha da derli toplu yazılabilir. İlk olarak, derleyici kendisi için obj1ve obj2değişkenlerinin olduğunu belirleyebilir Strings. İkincisi, yöntem kodunda yalnızca tek bir komutunuz varsa kaşlı ayraçlar ve dönüş ifadesi de atlanabilir.

Kısaltılmış versiyon şu şekilde olacaktır:

Comparator<String> comparator = (obj1, obj2) ->
   obj1.length()obj2.length();

Collections.sort(list, comparator);

Ve değişkeni kullanmak yerine comparatorhemen değerini kullanırsak, aşağıdaki sürümü elde ederiz:

Collections.sort(list, (obj1, obj2) ->  obj1.length()obj2.length() );

Buna ne dersin? Gereksiz bilgi içermeyen tek bir kod satırı - yalnızca değişkenler ve kod. Daha kısa yapmanın bir yolu yok! Yoksa var mı?



5. Nasıl çalışır?

Aslında, kod daha da kompakt bir şekilde yazılabilir. Ama bunun hakkında daha sonra.

Bir interface tipini tek metod ile kullanacağınız bir lambda ifadesi yazabilirsiniz .

Örneğin, kodda bir lambda ifadesi yazabilirsiniz çünkü yöntemin imzası şöyledir:Collections.sort(list, (obj1, obj2) -> obj1.length() - obj2.length());sort()

sort(Collection<T> colls, Comparator<T> comp)

Koleksiyonu sort yöntemine ilk argüman olarak ilettiğimizde ArrayList<String>, derleyici ikinci argümanın türünü belirleyebildi . Buradan da, bu arayüzün tek bir metodu olduğu sonucuna varılmıştır. Diğer her şey bir tekniktir.Comparator<String>int compare(String obj1, String obj2)