:: Java 8'de (çift kolonlu) operatör


956

Java 8 kaynağını araştırıyordum ve kodun bu kısmını çok şaşırtıcı buldum:

//defined in IntPipeline.java
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
    return evaluate(ReduceOps.makeInt(op));
}

@Override
public final OptionalInt max() {
    return reduce(Math::max); //this is the gotcha line
}

//defined in Math.java
public static int max(int a, int b) {
    return (a >= b) ? a : b;
}

Math::maxbir yöntem pointer gibi bir şey? Normal bir staticyöntem nasıl dönüştürülür IntBinaryOperator?


60
Derleyicinin sağladığınız işleve dayalı olarak otomatik arayüz oluşturma uygulamalarına sahip olması sözdizimsel şekerdir (lambda nesnesinin mevcut kod tabanlarıyla daha kolay kullanılmasını sağlamak için).
Neet

4
java.dzone.com/articles/java-lambda-expressions-vs yardımcı olabilir, konunun derinliklerine bakmadı
Pontus Backlund

8
@ Neet tam olarak "sözdizimsel şeker" değil, ne diyemezsen. yani "x, y için sözdizimsel şekerdir".
Ingo

6
@Ingo, her kullandığımda yeni bir lambda nesnesi oluşturur. TestingLambda$$Lambda$2/8460669ve TestingLambda$$Lambda$3/11043253iki çağrı üzerine yaratıldı.
Narendra Pathai

13
Lambdalar ve yöntem referansları "düz eski anonim iç sınıflar" değildir. Bkz. Programmers.stackexchange.com/a/181743/59134 . Evet, gerekirse yeni sınıflar ve örnekler anında, ancak gerekirse sadece oluşturulur.
Stuart Marks

Yanıtlar:


1022

Genellikle, reduceyöntem Math.max(int, int)aşağıdaki gibi kullanılarak çağrılır:

reduce(new IntBinaryOperator() {
    int applyAsInt(int left, int right) {
        return Math.max(left, right);
    }
});

Bu sadece aramak için çok sözdizimi gerektirir Math.max. Burada lambda ifadeleri devreye giriyor. Java 8'den beri aynı şeyi çok daha kısa bir şekilde yapmasına izin verilir:

reduce((int left, int right) -> Math.max(left, right));

Bu nasıl çalışıyor? Java derleyicisi, iki ints kabul eden ve bir döndüren bir yöntem uygulamak istediğinizi "algılar" int. Bu, tek arabirim yönteminin IntBinaryOperator( reducearamak istediğiniz yöntemin parametresi) resmi parametrelerine eşdeğerdir . Derleyici gerisini sizin için yapar - sadece uygulamak istediğinizi varsayar IntBinaryOperator.

Ancak Math.max(int, int)kendisi resmi gereklilikleri yerine getirdiği için IntBinaryOperatordoğrudan kullanılabilir. Java 7'de, bir yöntemin kendisinin bağımsız değişken olarak geçirilmesine izin veren bir sözdizimi bulunmadığından (yalnızca yöntem sonuçlarını geçemezsiniz, ancak hiçbir zaman yöntem başvurusunu geçemezsiniz), ::sözdizimi, başvuru yöntemlerine Java 8'de tanıtıldı:

reduce(Math::max);

Bunun çalışma zamanında JVM tarafından değil, derleyici tarafından yorumlanacağını unutmayın! Her üç kod parçacığı için farklı bayt kodlar üretmesine rağmen, bunlar semantik olarak eşittir, bu nedenle son ikisi IntBinaryOperatoryukarıdaki uygulamanın kısa (ve muhtemelen daha verimli) sürümleri olarak kabul edilebilir !

(Ayrıca bakınız Lambda İfadeleri Çevirisi )


489

::Yöntem Referansı olarak adlandırılır. Temel olarak tek bir yönteme referanstır. Yani mevcut bir metodu ismiyle ifade eder.

Kısa Açıklama :
Aşağıda statik bir yönteme başvuru örneği verilmiştir:

class Hey {
     public static double square(double num){
        return Math.pow(num, 2);
    }
}

Function<Double, Double> square = Hey::square;
double ans = square.apply(23d);

squaretıpkı nesne referansları gibi aktarılabilir ve gerektiğinde tetiklenebilir. Aslında, nesnelerin "normal" yöntemlerine referans olarak kolayca kullanılabilir static. Örneğin:

class Hey {
    public double square(double num) {
        return Math.pow(num, 2);
    }
}

Hey hey = new Hey();
Function<Double, Double> square = hey::square;
double ans = square.apply(23d);

FunctionYukarıdaki fonksiyonel bir arayüzdür . Tam ::olarak anlamak için fonksiyonel arayüzleri de anlamak önemlidir. Açıkçası, fonksiyonel bir arayüz sadece bir soyut yöntemle bir arayüzdür.

Fonksiyonel arayüzlerin örnekleri arasında Runnable, Callableve ActionListener.

FunctionYukarıda tek bir yöntem ile işlevsel bir arayüz: apply. Bir argüman alır ve bir sonuç üretir.


::S'nin harika olmasının nedeni şudur :

Yöntem referansları, lambda ifadeleri (...) ile aynı işleme sahip ifadelerdir, ancak bir yöntem gövdesi sağlamak yerine, varolan bir yöntemi adıyla ifade ederler.

Örneğin lambda gövdesini yazmak yerine

Function<Double, Double> square = (Double x) -> x * x;

Sadece yapabilirsin

Function<Double, Double> square = Hey::square;

Çalışma zamanında, bu iki squareyöntem birbiriyle tamamen aynı şekilde davranır. Bayt kodu aynı olabilir veya olmayabilir (yukarıdaki durumda aynı bayt kodu oluşturulur; yukarıdakileri derleyin ve kontrol edin javap -c).

Karşılanması gereken tek önemli kriter şudur: Sağladığınız yöntem, nesne başvurusu olarak kullandığınız işlevsel arabirimin yöntemine benzer bir imzaya sahip olmalıdır .

Aşağıdakiler yasa dışıdır:

Supplier<Boolean> p = Hey::square; // illegal

squarebir bağımsız değişken bekliyor ve a döndürüyor double. getYöntem Tedarikçi bir değer döndürür ancak bir argüman almaz. Böylece, bu bir hatayla sonuçlanır.

Bir yöntem referansı, fonksiyonel bir arayüzün yöntemini ifade eder. (Belirtildiği gibi, fonksiyonel arayüzlerin her birinde yalnızca bir yöntem olabilir).

Bazı örnekler: Tüketici'dekiaccept yöntem bir girdi alır, ancak hiçbir şey döndürmez.

Consumer<Integer> b1 = System::exit;   // void exit(int status)
Consumer<String[]> b2 = Arrays::sort;  // void sort(Object[] a)
Consumer<String> b3 = MyProgram::main; // void main(String... args)

class Hey {
    public double getRandom() {
        return Math.random();
    }
}

Callable<Double> call = hey::getRandom;
Supplier<Double> call2 = hey::getRandom;
DoubleSupplier sup = hey::getRandom;
// Supplier is functional interface that takes no argument and gives a result

Yukarıda, getRandomhiçbir argüman almaz ve a döndürür double. Bu nedenle şu kriterleri karşılayan herhangi bir fonksiyonel arayüz: argüman almayın ve geri dönüşdouble kullanılamaz.

Başka bir örnek:

Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = set::contains;
boolean exists = pred.test("leo");

Parametreli tiplerde :

class Param<T> {
    T elem;
    public T get() {
        return elem;
    }

    public void set(T elem) {
        this.elem = elem;
    }

    public static <E> E returnSame(E elem) {
        return elem;
    }
}

Supplier<Param<Integer>> obj = Param<Integer>::new;
Param<Integer> param = obj.get();
Consumer<Integer> c = param::set;
Supplier<Integer> s = param::get;

Function<String, String> func = Param::<String>returnSame;

Yöntem referansları farklı stillere sahip olabilir, ancak temelde hepsi aynı anlama gelir ve lambdas olarak görselleştirilebilir:

  1. Statik bir yöntem ( ClassName::methName)
  2. Belirli bir nesnenin örnek yöntemi ( instanceRef::methName)
  3. Belirli bir nesnenin süper yöntemi ( super::methName)
  4. Belirli bir türdeki rasgele bir nesnenin örnek yöntemi ( ClassName::methName)
  5. Sınıf yapıcı başvurusu ( ClassName::new)
  6. Dizi oluşturucu başvurusu ( TypeName[]::new)

Daha fazla referans için bkz. Http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html .


6
Açıklama için teşekkürler. Özetle: '::', bir FunctionalInterface (lambda) işlevini karşılayan bir yöntem ayıklamak için kullanın: ClassX :: staticMethodX veya instanceX :: instanceMethodX "
jessarah

55

Evet bu doğru. ::Operatör yöntemi olarak referans alınması için kullanılır. Bu nedenle, sınıfları kullanarak statik yöntemleri veya nesnelerden yöntemleri ayıklayabilirsiniz . Aynı operatör, inşaatçılar için bile kullanılabilir. Burada bahsedilen tüm durumlar, aşağıdaki kod örneğinde örneklenmiştir.

Oracle'ın resmi belgelerine buradan ulaşabilirsiniz .

Bu makaledeki JDK 8 değişikliklerine daha iyi bir genel bakış sağlayabilirsiniz . Gelen Yöntemi / Oluşturucu referans bölüm kodu bir örnektir da sağlanmıştır:

interface ConstructorReference {
    T constructor();
}

interface  MethodReference {
   void anotherMethod(String input);
}

public class ConstructorClass {
    String value;

   public ConstructorClass() {
       value = "default";
   }

   public static void method(String input) {
      System.out.println(input);
   }

   public void nextMethod(String input) {
       // operations
   }

   public static void main(String... args) {
       // constructor reference
       ConstructorReference reference = ConstructorClass::new;
       ConstructorClass cc = reference.constructor();

       // static method reference
       MethodReference mr = cc::method;

       // object method reference
       MethodReference mr2 = cc::nextMethod;

       System.out.println(cc.value);
   }
}

burada bulunan iyi bir açıklama: doanduyhai.wordpress.com/2012/07/14/…
Olimpiu POP

1
@RichardTingle method(Math::max);invokasyon ve yöntem tanımı gibi olurdu public static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}. Bu şekilde kullanılır.
Narendra Pathai

2
C # 'ı bilenler için DelegateType d = new DelegateType (MethodName);
Adrian Zanescu

27

Biraz geç görünüyor ama işte benim iki sentim. Bir lambda ifade anonim yöntemleri oluşturmak için kullanılır. Varolan bir yöntemi çağırmaktan başka bir şey yapmaz, ancak yönteme doğrudan adıyla başvurmak daha açıktır. Ve yöntem referansı olduğunu kullanılarak yöntemi başvuru operatörü yapmamızı sağlıyor ::.

Her çalışanın bir adı ve notu olan aşağıdaki basit sınıfı düşünün.

public class Employee {
    private String name;
    private String grade;

    public Employee(String name, String grade) {
        this.name = name;
        this.grade = grade;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }
}

Bazı yöntemlerle döndürülen çalışanların bir listesine sahip olduğumuzu ve çalışanları sınıflarına göre sıralamak istediğimizi varsayalım. Anonim sınıftan şu şekilde faydalanabileceğimizi biliyoruz :

    List<Employee> employeeList = getDummyEmployees();

    // Using anonymous class
    employeeList.sort(new Comparator<Employee>() {
           @Override
           public int compare(Employee e1, Employee e2) {
               return e1.getGrade().compareTo(e2.getGrade());
           }
    });

burada getDummyEmployee () yöntemi aşağıdaki gibi bir yöntemdir:

private static List<Employee> getDummyEmployees() {
        return Arrays.asList(new Employee("Carrie", "C"),
                new Employee("Fanishwar", "F"),
                new Employee("Brian", "B"),
                new Employee("Donald", "D"),
                new Employee("Adam", "A"),
                new Employee("Evan", "E")
                );
    }

Şimdi Karşılaştırıcının Fonksiyonel Arayüz olduğunu biliyoruz . Bir Fonksiyonel Arayüz (bir veya daha fazla varsayılan veya statik yöntemlerini içerebilir rağmen) tam olarak bir soyut yöntem ile biridir. Lambda ifadesi, @FunctionalInterfaceişlevsel bir arabirimin yalnızca bir soyut yönteme sahip olabilmesi için uygulama sağlar . Lambda ifadesini şu şekilde kullanabiliriz:

employeeList.sort((e1,e2) -> e1.getGrade().compareTo(e2.getGrade())); // lambda exp

Her şey iyi görünüyor ama ya sınıf Employeeda benzer bir yöntem sağlıyorsa:

public class Employee {
    private String name;
    private String grade;
    // getter and setter
    public static int compareByGrade(Employee e1, Employee e2) {
        return e1.grade.compareTo(e2.grade);
    }
}

Bu durumda, yöntem adının kullanılması daha açık olacaktır. Bu nedenle, yöntem referansını kullanarak doğrudan yönteme başvurabiliriz:

employeeList.sort(Employee::compareByGrade); // method reference

Gereğince docs yöntem referansların dört çeşitleri vardır:

+----+-------------------------------------------------------+--------------------------------------+
|    | Kind                                                  | Example                              |
+----+-------------------------------------------------------+--------------------------------------+
| 1  | Reference to a static method                          | ContainingClass::staticMethodName    |
+----+-------------------------------------------------------+--------------------------------------+
| 2  |Reference to an instance method of a particular object | containingObject::instanceMethodName | 
+----+-------------------------------------------------------+--------------------------------------+
| 3  | Reference to an instance method of an arbitrary object| ContainingType::methodName           |
|    | of a particular type                                  |                                      |  
+----+-------------------------------------------------------+--------------------------------------+
| 4  |Reference to a constructor                             | ClassName::new                       |
+------------------------------------------------------------+--------------------------------------+

25

::Java 8'de bulunan ve varolan bir sınıfın yöntemine başvurmak için kullanılan yeni bir işleçtir. Bir sınıfın statik yöntemlerine ve statik olmayan yöntemlerine başvurabilirsiniz.

Statik yöntemlere başvurmak için sözdizimi şöyledir:

ClassName :: methodName 

Statik olmayan yöntemlere başvurmak için sözdizimi

objRef :: methodName

Ve

ClassName :: methodName

Bir yönteme atıfta bulunmanın tek ön koşulu, yöntemin referans ile uyumlu olması gereken fonksiyonel bir arayüzde yöntemin mevcut olmasıdır.

Yöntem başvuruları, değerlendirildiğinde, işlevsel arabirimin bir örneğini oluşturur.

Bulunan: http://www.speakingcs.com/2014/08/method-references-in-java-8.html


22

Bu Java 8'de bir yöntem referansıdır. Oracle belgeleri burada .

Belgelerde belirtildiği gibi ...

Person :: yöntemByAge yöntem başvurusu, statik bir yönteme başvurudur.

Aşağıdaki, belirli bir nesnenin örnek yöntemine yapılan başvurunun bir örneğidir:

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }

    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}

ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName); 

MyComparisonProvider :: comparByName yöntem başvurusu, myComparisonProvider nesnesinin bir parçası olan CompareByName yöntemini çağırır. JRE, bu durumda (Kişi, Kişi) olan yöntem türü argümanlarını ihlal eder.


2
ancak 'CompareByAge' yöntemi statik değildir.
abbas

3
@abbas Nor karşılaştırmakByName. Bu nedenle, bu statik olmayan yöntemlere bir nesne kullanarak başvuru işleci üzerinden erişirsiniz. Statik olsaydı
Seshadri R

6

:: Yöntem referansları için operatör java 8'de tanıtıldı. Bir yöntem başvurusu, yalnızca ONE yöntemini çalıştıran lambda ifadesinin kısayol sözdizimidir. Bir yöntem başvurusunun genel sözdizimi şöyledir:

Object :: methodName

Anonim bir sınıf kullanmak yerine lambda ifadeleri kullanabileceğimizi biliyoruz . Ancak bazen, lambda ifadesi gerçekten sadece bazı yöntemlere bir çağrıdır, örneğin:

Consumer<String> c = s -> System.out.println(s);

Kodu daha net hale getirmek için, bu lambda ifadesini bir yöntem referansına dönüştürebilirsiniz:

Consumer<String> c = System.out::println;

3

:: yöntem referansları olarak bilinir. Diyelim ki satınalma sınıfı bir calculatePrice yöntemi çağırmak istiyoruz. Sonra şöyle yazabiliriz:

Purchase::calculatePrice

Aynı zamanda lambda ifadesini yazmanın kısa bir biçimi olarak da görülebilir. Çünkü yöntem referansları lambda ifadelerine dönüştürülür.


İç içe yöntem başvuruları yapabilir miyim? ör. gruplama (Sipariş :: müşteri :: ad)

bu şekilde iç içe yöntem referansı yapamazsınız
Kirby

3

Bu kaynağı çok ilginç buldum .

Aslında, öyle Lambda bir içine dönüşler Çift Colon . Çift Kolon daha okunabilir. Şu adımları takip ediyoruz:

AŞAMA 1:

// We create a comparator of two persons
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());

ADIM 2:

// We use the interference
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());

AŞAMA 3:

// The magic using method reference
Comparator c = Comparator.comparing(Person::getAge);

3
Person::getAge()Olması gerektiği gibi görünüyor Person::getAge.
Qwertiy

2

return reduce(Math::max);olan şey eşit içinreturn reduce(max());

Ama bu şu anlama geliyor:

IntBinaryOperator myLambda = (a, b)->{(a >= b) ? a : b};//56 keystrokes I had to type -_-
return reduce(myLambda);

Sadece edebilirsiniz 47 tuş vuruşlarını kaydetmek Eğer böyle yazarsanız

return reduce(Math::max);//Only 9 keystrokes ^_^

2

Burada birçok cevap iyi ::davranışı açıkladığından , ek olarak :: operatörün, örnek değişkenler için kullanılıyorsa, atıfta bulunan Fonksiyonel Arayüz ile tam olarak aynı imzası olması gerekmediğini açıklığa kavuşturmak istiyorum . Biz gerek varsayalım BinaryOperator türünü sahiptir testObject . Geleneksel olarak bu şekilde uygulanır:

BinaryOperator<TestObject> binary = new BinaryOperator<TestObject>() {

        @Override
        public TestObject apply(TestObject t, TestObject u) {

            return t;
        }
    };

Anonim uygulamada gördüğünüz gibi iki TestObject argümanı gerektirir ve bir TestObject nesnesi de döndürür. Bu koşulu ::operatörü kullanarak karşılamak için statik bir yöntemle başlayabiliriz:

public class TestObject {


    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

ve sonra arayın:

BinaryOperator<TestObject> binary = TestObject::testStatic;

Tamam iyi derlenmiş. Bir örnek yöntemine ihtiyacımız olursa ne olur? TestObject öğesini örnek yöntemiyle güncelleyelim:

public class TestObject {

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

Şimdi örneğe aşağıdaki gibi erişebiliriz:

TestObject testObject = new TestObject();
BinaryOperator<TestObject> binary = testObject::testInstance;

Bu kod iyi derler, ancak bir tanesinin altında değil:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Tutulmam bana "TestObject türünden statik olmayan yöntem testInstance (TestObject, TestObject) statik başvuru yapamaz ..."

Yeterince adil bir örnek yöntemi, ancak testInstanceaşağıdaki gibi aşırı yüklerseniz:

public class TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

Ve Çağrı yap:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Kod iyi derlenecektir. Çünkü testInstanceçift ​​bir yerine tek bir parametre ile çağıracaktır . Peki iki parametremiz ne oldu? Çıktıya izin verir ve bkz:

public class TestObject {

    public TestObject() {
        System.out.println(this.hashCode());
    }

    public final TestObject testInstance(TestObject t){
        System.out.println("Test instance called. this.hashCode:" 
    + this.hashCode());
        System.out.println("Given parameter hashCode:" + t.hashCode());
        return t;
    }

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

Hangi çıktı:

 1418481495  
 303563356  
 Test instance called. this.hashCode:1418481495
 Given parameter hashCode:303563356

Tamam, JVM param1.testInstance (param2) öğesini çağırmak için yeterince akıllıdır. testInstanceTestObject yerine başka bir kaynaktan kullanabilir miyiz, yani:

public class TestUtil {

    public final TestObject testInstance(TestObject t){
        return t;
    }
}

Ve Çağrı yap:

BinaryOperator<TestObject> binary = TestUtil::testInstance;

Sadece derlemez ve derleyici şunu söyler: "TestUtil türü testInstance (TestObject, TestObject) tanımlamaz" . Dolayısıyla, derleyici aynı türde değilse statik bir başvuru arar. Tamam, polimorfizm ne olacak? Son değiştiricileri kaldırır ve SubTestObject sınıfımızı eklersek :

public class SubTestObject extends TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

}

Ve Çağrı yap:

BinaryOperator<TestObject> binary = SubTestObject::testInstance;

Derleyici de olmayacak, derleyici hala statik referans arayacaktır. Ancak aşağıdaki kod geçtiği için iyi bir derleme olacaktır-bir test:

public class TestObject {

    public SubTestObject testInstance(Object t){
        return (SubTestObject) t;
    }

}

BinaryOperator<TestObject> binary = TestObject::testInstance;

* Sadece okuyorum bu yüzden anlamaya çalıştım, yanılıyorsam beni düzeltmekten çekinmeyin


2

Java-8'de Basit eserlerde Akış Düşürücü giriş olarak iki değer alan ve bazı hesaplamalardan sonra sonuç döndüren bir işlevdir. bu sonuç bir sonraki yinelemede beslenir.

Math: max işlevi durumunda, yöntem iletilen en fazla iki değeri döndürmeye devam eder ve sonunda elinizde en büyük sayı vardır.


1

Çalışma zamanında tam olarak aynı şekilde davranırlar.B bayt kodu aynı olabilir / olmayabilir (Yukarıdaki Incase için aynı bayt kodunu oluşturur (yukarıda belirtilen ve javaap -c'yi kontrol edin))

Çalışma zamanında tam olarak aynı davranırlar. Metod (math :: max); aynı matematiği oluşturur (yukarıdaki komplie ve javap -c'yi kontrol edin))


1

Eski Java sürümlerinde "::" veya lambd yerine şunları kullanabilirsiniz:

public interface Action {
    void execute();
}

public class ActionImpl implements Action {

    @Override
    public void execute() {
        System.out.println("execute with ActionImpl");
    }

}

public static void main(String[] args) {
    Action action = new Action() {
        @Override
        public void execute() {
            System.out.println("execute with anonymous class");
        }
    };
    action.execute();

    //or

    Action actionImpl = new ActionImpl();
    actionImpl.execute();
}

Veya yönteme geçme:

public static void doSomething(Action action) {
    action.execute();
}

1

Yani burada açıkçası overcomplicated yanıtların, ton görmek ve bu ifade yetersiz.

Cevap oldukça basit: :: buna Yöntem Referansları denir https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

Böylece kopyalayıp yapıştırmayacağım, bağlantıda, tabloya ilerlediğinizde tüm bilgileri bulabilirsiniz.


Şimdi Yöntem Referanslarının ne olduğuna kısaca göz atalım:

A :: B biraz aşağıdaki ikame içi lambda ifade : (parametreler ...) -> AB (parametreler ...)

Bunu sorularınızla ilişkilendirmek için bir java lambda ifadesini anlamak gerekir. Bu zor değil.

Satır içi bir lambda ifadesi, tanımlanmış bir işlevsel arabirime (1 yöntemden daha fazla ve daha az olmayan bir arabirimdir) benzer . Ne demek istediğime kısa bir göz atalım:

InterfaceX f = (x) -> x*x; 

InterfaceX işlevsel bir arayüz olmalıdır. Herhangi bir işlevsel arayüz, o derleyici için InterfaceX hakkında önemli olan tek şey formatı tanımlamanızdır:

InterfaceX şunlardan herhangi biri olabilir:

interface InterfaceX
{
    public Integer callMe(Integer x);
}

veya bu

interface InterfaceX
{
    public Double callMe(Integer x);
}

veya daha genel:

interface InterfaceX<T,U>
{
    public T callMe(U x);
}

İlk sunduğumuz vakayı ve daha önce tanımladığımız satır içi lambda ifadesini ele alalım.

Java 8'den önce, bunu benzer şekilde tanımlamış olabilirsiniz:

 InterfaceX o = new InterfaceX(){
                     public int callMe (int x, int y) 
                       {
                        return x*x;
                       } };

İşlevsel olarak, aynı şey. Fark, derleyicinin bunu algılama biçiminde daha fazladır.

Şimdi satır içi lambda ifadesine baktığımıza, Yöntem Referanslarına (: :) dönelim. Diyelim ki böyle bir sınıfınız var:

class Q {
        public static int anyFunction(int x)
             {
                 return x+5;
             } 
        }

AnyFunctions yöntemi , InterfaceX callMe ile aynı türlere sahip olduğundan , bu ikisini bir Yöntem Referansı ile eşitleyebiliriz .

Bunu şöyle yazabiliriz:

InterfaceX o =  Q::anyFunction; 

ve buna eşdeğer:

InterfaceX o = (x) -> Q.anyFunction(x);

Yöntem Referanslarının harika bir avantajı ve avantajı, ilk olarak, değişkenlere atanana kadar, bunların tipik olmayan olmasıdır. Böylece bunları eşdeğer görünümlü (aynı tanımlı tiplere sahiptir) fonksiyonel arayüze parametre olarak aktarabilirsiniz. Bu tam olarak davanızda olan şey


1

Önceki yanıtlar, ::yöntem referansının ne yaptığı konusunda oldukça eksiksizdir . Özetle, bir yönteme (veya kurucuya) yürütmeden başvurmak için bir yol sağlar ve değerlendirildiğinde, hedef tip bağlamını sağlayan işlevsel arabirimin bir örneğini oluşturur.

Aşağıda, yöntem başvurusunun bir ArrayListWITH ve WITHOUT değerinde maksimum değere sahip bir nesne bulmak için iki örnek verilmiştir ::. Açıklamalar aşağıdaki yorumlarda yer almaktadır.


KULLANMADAN ::

import java.util.*;

class MyClass {
    private int val;
    MyClass (int v) { val = v; }
    int getVal() { return val; }
}

class ByVal implements Comparator<MyClass> {
    // no need to create this class when using method reference
    public int compare(MyClass source, MyClass ref) {
        return source.getVal() - ref.getVal();
    }
}

public class FindMaxInCol {
    public static void main(String args[]) {
        ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
        myClassList.add(new MyClass(1));
        myClassList.add(new MyClass(0));
        myClassList.add(new MyClass(3));
        myClassList.add(new MyClass(6));

        MyClass maxValObj = Collections.max(myClassList, new ByVal());
    }
}

Kullanımı ile ::

import java.util.*;

class MyClass {
    private int val;
    MyClass (int v) { val = v; }
    int getVal() { return val; }
}

public class FindMaxInCol {
    static int compareMyClass(MyClass source, MyClass ref) {
        // This static method is compatible with the compare() method defined by Comparator. 
        // So there's no need to explicitly implement and create an instance of Comparator like the first example.
        return source.getVal() - ref.getVal();
    }

    public static void main(String args[]) {
        ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
        myClassList.add(new MyClass(1));
        myClassList.add(new MyClass(0));
        myClassList.add(new MyClass(3));
        myClassList.add(new MyClass(6));

        MyClass maxValObj = Collections.max(myClassList, FindMaxInCol::compareMyClass);
    }
}

-1

Çift kolon yani ::operatör Java 8'de bir yöntem referansı olarak kullanılmıştır . Yöntem referansı, mevcut yönteme adıyla atıfta bulunmak için kullanılan bir lambda ifadesi biçimidir .

sınıfadı :: methodName

örn: -

  • stream.forEach(element -> System.out.println(element))

Double Colon kullanarak ::

  • stream.forEach(System.out::println(element))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.