Pertanyaan :: operator (dua titik dua) di Java 8


Saya menjelajahi sumber Java 8 dan menemukan kode khusus 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;
}

Aku s Math::max sesuatu seperti penunjuk metode? Bagaimana cara normal static metode dikonversi menjadi IntBinaryOperator?


747
2017-11-15 12:46


asal


Jawaban:


Biasanya, seseorang akan memanggil reduce metode 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 hanya menelepon Math.max. Di situlah ekspresi lambda ikut bermain. Karena Java 8 diperbolehkan melakukan hal yang sama dengan cara yang jauh lebih pendek:

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

Bagaimana cara kerjanya? Java compiler "mendeteksi", bahwa Anda ingin menerapkan metode yang menerima dua intdan kembalikan satu int. Ini setara dengan parameter formal dari satu-satunya metode antarmuka IntBinaryOperator (parameter metode reduce Anda ingin memanggil). Jadi kompilator melakukan sisanya untuk Anda - itu hanya mengasumsikan Anda ingin mengimplementasikan IntBinaryOperator.

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

reduce(Math::max);

Perhatikan bahwa ini akan ditafsirkan oleh kompiler, bukan oleh JVM saat runtime! Meskipun menghasilkan bytecode yang berbeda untuk ketiga cuplikan kode, keduanya secara semantis sama, sehingga dua yang terakhir dapat dianggap sebagai versi pendek (dan mungkin lebih efisien) dari IntBinaryOperator implementasi di atas!

(Lihat juga Terjemahan Lambda Expressions)


826
2017-11-15 13:08



:: disebut Referensi Metode. Ini pada dasarnya adalah referensi ke metode tunggal. yaitu merujuk pada metode yang ada berdasarkan 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);

square dapat diedarkan seperti referensi obyek dan memicu saat dibutuhkan. Bahkan, itu dapat digunakan secara sempurna sebagai referensi ke metode normal suatu objek dan bukan hanya static satu.

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

Function di atas adalah a antarmuka fungsional. Baiklah untuk menjelaskan sepenuhnya ::, penting untuk memahami antarmuka fungsional. Terus terang, antarmuka fungsi adalah antarmuka hanya dengan satu metode abstrak.

Sebagai contoh: Runnable, Callable, ActionListener dan sebagainya.

Function di atas adalah antarmuka fungsional hanya dengan satu metode apply. Diperlukan satu argumen dan menghasilkan sebuah hasil.


Alasan mengapa :: luar biasa adalah karena:

Referensi metode adalah ekspresi yang memiliki perlakuan yang sama seperti ekspresi lambda (...), tetapi alih-alih menyediakan badan metode, mereka merujuk metode yang ada berdasarkan namanya.

i.e. Seperti menulis lambda body:

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

Anda cukup melakukan:

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

Saat runtime mereka berperilaku persis sama. Bytecode mungkin / tidak sama (Untuk kasus di atas, ia menghasilkan bytecode yang sama (kompilasi di atas dan periksa javap -c))

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

Di bawah ini ilegal:

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

square mengharapkan sebuah argumen dan mengembalikan dua kali lipat. get metode dalam Pemasok mengharapkan sebuah argumen tetapi tidak mengembalikan apa pun. Jadi ini adalah kesalahan.

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

Beberapa contoh lainnya: accept metode dalam Konsumen mengambil masukan 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

Atas getRandom tidak perlu berdebat dan mengembalikan dua kali lipat. Jadi setiap antarmuka fungsional yang memenuhi kriteria: jangan berdebat dan kembali lagi 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 Jenis-Jenis Yang Ter-Parameterisasi:

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 diperoleh dalam gaya yang berbeda, tetapi pada dasarnya mereka semua berarti 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 instan dari objek arbitrer dari tipe tertentu (ClassName::methName)
  5. Referensi konstruktor kelas (ClassName::new)
  6. Referensi konstruktor array (TypeName[]::new)

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


382
2018-03-07 08:47



Ya itu benar. Itu :: operator digunakan untuk referensi metode. Jadi, seseorang bisa mengekstrak statis metode dari kelas dengan menggunakan 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 sini.

Anda dapat memiliki gambaran yang lebih baik tentang perubahan 8 JDK ini artikel. Dalam Referensi Metode / Pembimbing Bagian 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);
   }
}

49
2017-11-15 12:51



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

Untuk merujuk metode statis, sintaksnya adalah:

ClassName :: methodName 

Untuk merujuk metode non-statis, sintaksnya

objRef :: methodName

Dan

ClassName :: methodName

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

Referensi metode, ketika dievaluasi, buat instance antarmuka fungsional.

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


22
2017-09-05 07:09



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

Sebagaimana tercantum dalam dokumentasi ...

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

Berikut ini adalah contoh referensi ke metode instance a   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. The JRE menyimpulkan   argumen jenis metode, yang dalam hal ini adalah (Orang, Orang).


18
2017-11-15 12:52



Tampaknya sedikit terlambat tetapi di sini adalah dua sen saya. SEBUAH ekspresi lambda digunakan untuk membuat metode anonim. Ia tidak melakukan apa pun kecuali memanggil metode yang sudah ada, tetapi lebih jelas untuk merujuk ke metode langsung dengan namanya. Dan referensi metode memungkinkan kami 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 oleh beberapa metode dan kami ingin mengurutkan karyawan berdasarkan nilai mereka. Kita tahu kita bisa memanfaatkannya 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("Farhan", "F"),
                new Employee("Brian", "B"),
                new Employee("Donald", "D"),
                new Employee("Adam", "A"),
                new Employee("Evan", "E")
                );
    }

Sekarang kita tahu itu Pembanding adalah Antarmuka Fungsional. SEBUAH Antarmuka Fungsional adalah satu dengan tepat satu metode abstrak (meskipun mungkin berisi satu atau lebih metode default atau statis). Jadi kita bisa menggunakan ekspresi lambda sebagai:

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

Tampaknya semua baik tetapi bagaimana jika kelas Employee juga 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. Maka 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                       |
+------------------------------------------------------------+--------------------------------------+

9
2018-04-21 06:09



:: Operator diperkenalkan di java 8 untuk referensi metode. Referensi metode adalah sintaks singkat 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. Tapi terkadang, ekspresi lambda benar-benar hanya panggilan untuk beberapa metode, misalnya:

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

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

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

4
2018-03-22 06:18



The :: dikenal sebagai referensi metode. Katakanlah kita ingin memanggil metode countingPrice pembelian kelas. Kemudian kita dapat menulisnya sebagai:

Purchase::calculatePrice

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


3
2017-11-15 18:45