:: (titik dua) operator di Java 8

956

Saya menjelajahi sumber Java 8 dan menemukan bagian kode ini sangat mengejutkan:

//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;
}

Apakah Math::maxsesuatu seperti penunjuk metode? Bagaimana cara staticmetode normal dikonversi IntBinaryOperator?

Narendra Pathai
sumber
60
Ini sintaksis untuk memiliki implementasi antarmuka penghasil-otomatis kompiler yang didasarkan pada fungsi yang Anda berikan (untuk membuat seluruh lambda lebih mudah digunakan dengan basis kode yang ada).
Neet
4
java.dzone.com/articles/java-lambda-expressions-vs mungkin membantu, tidak melihat terlalu jauh ke dalam topik
Pontus Backlund
8
@Neet itu tidak sepenuhnya "gula sintaksis", kecuali Anda dapat mengatakan untuk apa. yaitu "x adalah gula sintaksis untuk y".
Ingo
6
@Ingo itu menciptakan objek lambda baru setiap kali saya menggunakannya. TestingLambda$$Lambda$2/8460669dan TestingLambda$$Lambda$3/11043253dibuat di dua doa.
Narendra Pathai
13
Lambdas dan referensi metode bukanlah "kelas dalam anonim lama yang sederhana". Lihat programmers.stackexchange.com/a/181743/59134 . Ya, jika perlu, kelas dan instance baru dibuat secara langsung, jika perlu, tetapi hanya jika perlu.
Stuart Marks

Jawaban:

1023

Biasanya, seseorang akan memanggil reducemetode menggunakan Math.max(int, int)sebagai berikut:

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

Itu membutuhkan banyak sintaks untuk menelepon Math.max. Di situlah ekspresi lambda ikut bermain. Karena Java 8 diizinkan untuk melakukan hal yang sama dengan cara yang jauh lebih pendek:

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

Bagaimana cara kerjanya? Kompiler java "mendeteksi", bahwa Anda ingin menerapkan metode yang menerima dua intdan mengembalikan satuint . Ini sama dengan parameter formal dari satu-satunya metode antarmuka IntBinaryOperator(parameter metode reduceyang ingin Anda panggil). Jadi kompiler melakukan sisanya untuk Anda - itu hanya mengasumsikan Anda ingin menerapkan IntBinaryOperator.

Tetapi karena Math.max(int, int)memenuhi persyaratan formal IntBinaryOperator, itu dapat digunakan secara langsung. Karena Java 7 tidak memiliki sintaks yang memungkinkan metode itu sendiri untuk diteruskan sebagai argumen (Anda hanya bisa melewati hasil metode, tetapi tidak pernah referensi metode),:: sintaks diperkenalkan di Java 8 untuk metode referensi:

reduce(Math::max);

Perhatikan bahwa ini akan ditafsirkan oleh kompiler, bukan oleh JVM saat runtime! Walaupun ia menghasilkan bytecode yang berbeda untuk ketiga snipet kode, mereka secara semantik sama, sehingga dua yang terakhir dapat dianggap sebagai versi pendek (dan mungkin lebih efisien) dari IntBinaryOperatorimplementasi di atas!

(Lihat juga Terjemahan dari Ekspresi Lambda )

isnot2bad
sumber
489

::disebut Referensi Metode. Ini pada dasarnya adalah referensi ke metode tunggal. Yaitu mengacu pada metode yang ada dengan nama.

Penjelasan Singkat :
Di bawah ini adalah contoh referensi ke metode statis:

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

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

squaredapat diedarkan seperti referensi objek dan dipicu saat diperlukan. Bahkan, itu bisa dengan mudah digunakan sebagai referensi untuk metode objek "normal" static. Sebagai contoh:

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);

Functiondi atas adalah antarmuka fungsional . Untuk memahami sepenuhnya ::, penting untuk memahami antarmuka fungsional juga. Jelas, antarmuka fungsional adalah antarmuka dengan hanya satu metode abstrak.

Contoh antarmuka fungsional meliputi Runnable, Callable, dan ActionListener.

Functiondi atas adalah antarmuka fungsional dengan hanya satu metode: apply. Dibutuhkan satu argumen dan menghasilkan suatu hasil.


Alasan mengapa ::s mengagumkan adalah bahwa :

Referensi metode adalah ekspresi yang memiliki perlakuan yang sama dengan ekspresi lambda (...), tetapi alih-alih menyediakan tubuh metode, mereka merujuk metode yang ada dengan nama.

Misalnya alih-alih menulis badan lambda

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

Anda cukup melakukannya

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

Saat runtime, kedua squaremetode ini berperilaku sama persis satu sama lain. Bytecode mungkin atau mungkin tidak sama (walaupun, untuk kasus di atas, bytecode yang sama dihasilkan; kompilasi di atas dan periksa dengan javap -c).

Satu-satunya kriteria utama yang harus dipenuhi adalah: metode yang Anda berikan harus memiliki tanda tangan yang mirip dengan metode antarmuka fungsional yang Anda gunakan sebagai referensi objek .

Di bawah ini ilegal:

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

squaremengharapkan argumen dan mengembalikan a double. The getmetode dalam Pemasok mengembalikan nilai tetapi tidak mengambil argumen. Dengan demikian, ini menghasilkan kesalahan.

Referensi metode mengacu pada metode antarmuka fungsional. (Seperti yang disebutkan, antarmuka fungsional masing-masing hanya dapat memiliki satu metode).

Beberapa contoh lagi: acceptmetode di Consumer mengambil input tetapi tidak mengembalikan apa pun.

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

Di atas, getRandomtidak mengambil argumen dan mengembalikan a double. Jadi setiap antarmuka fungsional yang memenuhi kriteria: take no argumen and returndouble dapat digunakan.

Contoh lain:

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

Dalam hal tipe parameter :

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;

Referensi metode dapat memiliki gaya yang berbeda, tetapi pada dasarnya mereka semua memiliki makna yang sama dan hanya dapat divisualisasikan sebagai lambda:

  1. Metode statis ( ClassName::methName)
  2. Metode instance dari objek tertentu ( instanceRef::methName)
  3. Metode super dari objek tertentu ( super::methName)
  4. Metode contoh objek sewenang-wenang dari jenis tertentu (ClassName::methName )
  5. Referensi konstruktor kelas (ClassName::new )
  6. Referensi konstruktor array (TypeName[]::new )

Untuk referensi lebih lanjut, lihat http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html .

Jatin
sumber
6
Terima kasih atas penjelasannya. Singkatnya: '::' digunakan untuk mengekstraksi metode yang memenuhi FunctionalInterface (lambda): ClassX :: staticMethodX, atau instanceX :: instanceMethodX "
jessarah
55

Ya itu benar. The ::operator yang digunakan untuk metode referensi. Jadi, seseorang dapat mengekstrak metode statis dari kelas dengan menggunakannya atau metode dari objek. Operator yang sama dapat digunakan bahkan untuk konstruktor. Semua kasus yang disebutkan di sini dicontohkan dalam contoh kode di bawah ini.

Dokumentasi resmi dari Oracle dapat ditemukan di sini .

Anda dapat memiliki gambaran umum yang lebih baik tentang perubahan JDK 8 di artikel ini . Di bagian Metode / Konstruktor referensi contoh kode juga disediakan:

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);
   }
}
Olimpiu POP
sumber
penjelasan yang baik adalah yang ditemukan di sini: doanduyhai.wordpress.com/2012/07/14/…
Olimpiu POP
1
@RichardTingle method(Math::max);adalah doa dan definisi metode akan seperti public static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}. Begitulah cara menggunakannya.
Narendra Pathai
2
Bagi mereka yang akrab dengan C # ini mirip dengan DelegateType d = DelegateType baru (MethodName);
Adrian Zanescu
27

Sepertinya sedikit terlambat tetapi ini adalah dua sen saya. Sebuah ekspresi lambda digunakan untuk membuat metode anonim. Itu tidak melakukan apa-apa selain memanggil metode yang sudah ada, tetapi lebih jelas untuk merujuk ke metode langsung dengan namanya. Dan referensi metode memungkinkan kita untuk melakukannya menggunakan operator referensi-metode ::.

Pertimbangkan kelas sederhana berikut di mana setiap karyawan memiliki nama dan nilai.

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;
    }
}

Misalkan kita memiliki daftar karyawan yang dikembalikan dengan beberapa metode dan kami ingin menyortir karyawan berdasarkan peringkat mereka. Kami tahu kami dapat menggunakan kelas anonim sebagai:

    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());
           }
    });

di mana getDummyEmployee () adalah beberapa metode seperti:

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")
                );
    }

Sekarang kita tahu bahwa Pembanding adalah Antarmuka Fungsional. Sebuah Antarmuka Fungsional adalah satu dengan tepat satu metode abstrak (meskipun mungkin mengandung satu atau lebih standar atau statis metode). Ekspresi Lambda menyediakan implementasi @FunctionalInterfacesehingga antarmuka fungsional hanya dapat memiliki satu metode abstrak. Kita dapat menggunakan ekspresi lambda sebagai:

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

Tampaknya semua baik tetapi bagaimana jika kelas Employeejuga menyediakan metode serupa:

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);
    }
}

Dalam hal ini menggunakan nama metode itu sendiri akan lebih jelas. Karenanya kita dapat langsung merujuk ke metode dengan menggunakan referensi metode sebagai:

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

Sesuai dokumen ada empat jenis referensi metode:

+----+-------------------------------------------------------+--------------------------------------+
|    | 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                       |
+------------------------------------------------------------+--------------------------------------+
akhil_mittal
sumber
25

::adalah operator baru yang termasuk dalam Java 8 yang digunakan untuk merujuk metode dari kelas yang ada. Anda bisa merujuk metode statis dan metode non-statis suatu kelas.

Untuk merujuk metode statis, sintaksnya adalah:

ClassName :: methodName 

Untuk merujuk metode non-statis, sintaksnya adalah

objRef :: methodName

Dan

ClassName :: methodName

Satu-satunya prasyarat untuk merujuk metode adalah metode itu ada di antarmuka fungsional, yang harus kompatibel dengan referensi metode.

Referensi metode, ketika dievaluasi, membuat instance antarmuka fungsional.

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

sreenath
sumber
22

Ini adalah referensi metode di Java 8. Dokumentasi oracle ada di sini .

Sebagaimana dinyatakan dalam dokumentasi ...

Referensi metode Person :: compareByAge adalah referensi ke metode statis.

Berikut ini adalah contoh referensi ke metode instance objek tertentu:

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); 

Referensi metode myComparisonProvider :: compareByName memanggil metode compareByName yang merupakan bagian dari objek myComparisonProvider. JRE menyimpulkan argumen tipe metode, yang dalam hal ini adalah (Orang, Orang).

david99world
sumber
2
tetapi metode 'compareByAge' tidak statis.
abbas
3
@abbas Nor juga tidak sebandingByName. Karenanya, Anda mengakses metode non-statis ini melalui operator referensi menggunakan objek. Jika mereka statis, Anda bisa menggunakan nama kelas seperti ComparisionProvider :: someStaticMethod
Seshadri R
6

:: Operator diperkenalkan di java 8 untuk referensi metode. Referensi metode adalah sintaks steno untuk ekspresi lambda yang mengeksekusi hanya satu metode. Berikut sintaks umum referensi metode:

Object :: methodName

Kami tahu bahwa kami dapat menggunakannya ekspresi lambda alih-alih menggunakan kelas anonim. Tetapi terkadang, ekspresi lambda benar-benar hanya panggilan ke beberapa metode, misalnya:

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

Untuk membuat kode lebih jelas, Anda bisa mengubah ekspresi lambda menjadi referensi metode:

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

:: dikenal sebagai referensi metode. Katakanlah kita ingin memanggil metode CalculPrice dari Pembelian kelas. Maka kita dapat menuliskannya sebagai:

Purchase::calculatePrice

Ini juga dapat dilihat sebagai bentuk singkat penulisan ekspresi lambda Karena referensi metode diubah menjadi ekspresi lambda.

Sonu
sumber
Bisakah saya membuat referensi metode bersarang? misal groupingBy (Pesan :: pelanggan :: nama)
Anda tidak dapat membuat referensi metode bersarang dengan cara itu
Kirby
3

Saya menemukan sumber ini sangat menarik.

Bahkan, itu adalah Lambda yang berubah menjadi Double Colon . Double Colon lebih mudah dibaca. Kami mengikuti langkah-langkah itu:

LANGKAH 1:

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

LANGKAH 2:

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

LANGKAH3:

// The magic using method reference
Comparator c = Comparator.comparing(Person::getAge);
Houssem Badri
sumber
3
Sepertinya Person::getAge()harus Person::getAge.
Qwertiy
2

return reduce(Math::max);adalah TIDAK SAMA untukreturn reduce(max());

Tapi artinya, kira-kira seperti ini:

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

Anda hanya dapat menyimpan 47 penekanan tombol jika Anda menulis seperti ini

return reduce(Math::max);//Only 9 keystrokes ^_^
Jude Niroshan
sumber
2

Karena banyak jawaban di sini menjelaskan ::perilaku yang baik , saya juga ingin menjelaskan bahwa :: operator tidak perlu memiliki tanda tangan yang sama persis dengan Fungsional Interface yang merujuk jika digunakan untuk variabel instan . Mari kita asumsikan kita membutuhkan BinaryOperator yang memiliki tipe TestObject . Secara tradisional ini diterapkan seperti ini:

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

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

            return t;
        }
    };

Seperti yang Anda lihat dalam implementasi anonim itu membutuhkan dua argumen TestObject dan mengembalikan objek TestObject juga. Untuk memenuhi kondisi ini dengan menggunakan ::operator kita dapat mulai dengan metode statis:

public class TestObject {


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

lalu panggil:

BinaryOperator<TestObject> binary = TestObject::testStatic;

Ok itu dikompilasi dengan baik. Bagaimana jika kita membutuhkan metode instan? Mari perbarui TestObject dengan metode instan:

public class TestObject {

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

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

Sekarang kita dapat mengakses instance seperti di bawah ini:

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

Kode ini dikompilasi dengan baik, tetapi di bawahnya tidak:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Gerhana saya memberi tahu saya "Tidak dapat membuat referensi statis ke metode pengujian non-statisInstance (TestObject, TestObject) dari jenis TestObject ..."

Cukup adil, ini adalah metode instan, tetapi jika kita kelebihan testInstanceseperti di bawah ini:

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;
    }
}

Dan telepon:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Kode hanya akan dikompilasi dengan baik. Karena itu akan memanggil testInstancedengan parameter tunggal alih-alih ganda. Ok jadi apa yang terjadi dua parameter kita? Mari cetak dan lihat:

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;
    }
}

Yang akan menghasilkan:

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

Ok jadi JVM cukup pintar untuk memanggil param1.testInstance (param2). Bisakah kita menggunakan testInstancedari sumber lain tetapi bukan dari TestObject, yaitu:

public class TestUtil {

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

Dan telepon:

BinaryOperator<TestObject> binary = TestUtil::testInstance;

Itu tidak akan dikompilasi dan kompiler akan memberi tahu: "Jenis TestUtil tidak mendefinisikan testInstance (TestObject, TestObject)" . Jadi kompiler akan mencari referensi statis jika bukan tipe yang sama. Ok bagaimana dengan polimorfisme? Jika kami menghapus pengubah akhir dan menambahkan kelas SubTestObject kami :

public class SubTestObject extends TestObject {

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

}

Dan telepon:

BinaryOperator<TestObject> binary = SubTestObject::testInstance;

Ini tidak akan dikompilasi juga, kompiler akan tetap mencari referensi statis. Tetapi kode di bawah ini akan dikompilasi dengan baik karena ia lulus is-a test:

public class TestObject {

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

}

BinaryOperator<TestObject> binary = TestObject::testInstance;

* Saya baru belajar, jadi saya mencari tahu dengan mencoba dan melihat, jangan ragu untuk mengoreksi saya jika saya salah

HRgiger
sumber
2

Dalam java-8 Streams Reducer dalam karya sederhana adalah fungsi yang mengambil dua nilai sebagai input dan mengembalikan hasil setelah beberapa perhitungan. hasil ini dimasukkan dalam iterasi berikutnya.

dalam hal Matematika: fungsi maks, metode terus mengembalikan maks dari dua nilai yang diteruskan dan pada akhirnya Anda memiliki angka terbesar di tangan.

Pramod
sumber
1

Pada saat runtime mereka berperilaku sama persis. Bytecode mungkin / tidak sama (Untuk Incase di atas, ia menghasilkan bytecode yang sama (sesuai di atas dan periksa javaap -c;))

Saat runtime mereka berperilaku sama persis. Metode (matematika :: maks) ;, menghasilkan matematika yang sama (sesuai di atas dan periksa javap -c;))

Alfa khatoon
sumber
1

Dalam versi Java yang lebih lama, alih-alih "::" atau lambd, Anda dapat menggunakan:

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();
}

Atau meneruskan ke metode:

public static void doSomething(Action action) {
    action.execute();
}
Kamil Tomasz Jarmusik
sumber
1

Jadi saya melihat di sini banyak jawaban yang terlalu rumit, dan itu meremehkan.

Jawabannya cukup sederhana: :: itu disebut Referensi Metode https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

Jadi saya tidak akan menyalin-menempel, pada tautan, Anda dapat menemukan semua informasi jika Anda menggulir ke bawah ke tabel.


Sekarang, mari kita lihat apa itu Referensi Metode:

A :: B agak mengganti ekspresi lambda inline berikut : (params ...) -> AB (params ...)

Untuk menghubungkan ini dengan pertanyaan Anda, Anda perlu memahami ekspresi java lambda. Itu tidak sulit.

Ekspresi lambda inline mirip dengan antarmuka fungsional yang ditentukan (yang merupakan antarmuka yang memiliki tidak lebih dan tidak kurang dari 1 metode) . Mari kita lihat apa yang saya maksud:

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

InterfaceX harus berupa antarmuka fungsional. Antarmuka fungsional apa pun, satu-satunya hal yang penting tentang InterfaceX untuk kompiler itu adalah Anda menentukan formatnya:

InterfaceX dapat berupa semua ini:

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

atau ini

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

atau lebih umum:

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

Mari kita ambil contoh kasus pertama dan ekspresi lambda sebaris yang telah kita definisikan sebelumnya.

Sebelum Java 8, Anda bisa mendefinisikannya dengan cara ini:

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

Secara fungsional, itu hal yang sama. Perbedaannya lebih pada bagaimana kompiler memahami ini.

Sekarang kita melihat ekspresi lambda sebaris, mari kita kembali ke Referensi Metode (: :). Katakanlah Anda memiliki kelas seperti ini:

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

Karena metode anyFunctions memiliki tipe yang sama dengan InterfaceX callMe , kita dapat menyamakan keduanya dengan Referensi Metode.

Kita bisa menulis seperti ini:

InterfaceX o =  Q::anyFunction; 

dan itu setara dengan ini:

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

Suatu hal yang keren dan keuntungan dari Referensi Metode adalah bahwa pada awalnya, sampai Anda menetapkannya ke variabel, mereka tidak ada artinya. Jadi, Anda dapat mengirimkannya sebagai parameter ke antarmuka fungsional yang tampak setara (memiliki tipe yang sama). Itulah tepatnya yang terjadi dalam kasus Anda

Nertan Lucian
sumber
1

Jawaban sebelumnya cukup lengkap mengenai apa yang ::dilakukan referensi metode. Singkatnya, ini menyediakan cara untuk merujuk ke metode (atau konstruktor) tanpa mengeksekusi itu, dan ketika dievaluasi, itu menciptakan sebuah instance dari antarmuka fungsional yang menyediakan konteks tipe target.

Di bawah ini adalah dua contoh untuk menemukan objek dengan nilai maksimum dalam ArrayListDENGAN dan TANPA menggunakan ::referensi metode. Penjelasannya ada di komentar di bawah ini.


TANPA penggunaan ::

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());
    }
}

DENGAN penggunaan ::

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);
    }
}
Liutong Chen
sumber
-1

Double colon yaitu ::operator diperkenalkan di Java 8 sebagai referensi metode . Referensi metode adalah bentuk ekspresi lambda yang digunakan untuk merujuk metode yang ada dengan namanya.

classname :: methodName

ex:-

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

Dengan menggunakan Double Colon ::

  • stream.forEach(System.out::println(element))
ishant kulshreshtha
sumber