Bagaimana cara menetapkan metode yang menggunakan lambda sebagai parameter di Java 8?

363

Di Java 8, metode dapat dibuat sebagai ekspresi Lambda dan dapat dilewatkan dengan referensi (dengan sedikit kerja di bawah tenda). Ada banyak contoh online dengan lambdas yang dibuat dan digunakan dengan metode, tetapi tidak ada contoh bagaimana membuat metode menggunakan lambda sebagai parameter. Apa sintaks untuk itu?

MyClass.method((a, b) -> a+b);


class MyClass{
  //How do I define this method?
  static int method(Lambda l){
    return l(5, 10);
  }
}
Marius
sumber
29
Pertanyaan bagus. Dan Anda benar: Tidak ada tutorial yang mengandung bagian itu.
Martin

Jawaban:

247

Lambdas murni merupakan konstruksi situs-panggilan: penerima lambda tidak perlu tahu bahwa Lambda terlibat, melainkan menerima Antarmuka dengan metode yang sesuai.

Dengan kata lain, Anda mendefinisikan atau menggunakan antarmuka fungsional (yaitu antarmuka dengan metode tunggal) yang menerima dan mengembalikan apa yang Anda inginkan.

Untuk Java 8 ini hadir dengan serangkaian tipe antarmuka yang umum digunakan java.util.function(terima kasih kepada Maurice Naftalin untuk petunjuk tentang JavaDoc).

Untuk kasus penggunaan khusus ini ada java.util.function.IntBinaryOperatordengan satu int applyAsInt(int left, int right)metode , sehingga Anda bisa menulis Anda methodseperti ini:

static int method(IntBinaryOperator op){
    return op.applyAsInt(5, 10);
}

Tapi Anda bisa mendefinisikan antarmuka Anda sendiri dan menggunakannya seperti ini:

public interface TwoArgIntOperator {
    public int op(int a, int b);
}

//elsewhere:
static int method(TwoArgIntOperator operator) {
    return operator.op(5, 10);
}

Menggunakan antarmuka Anda sendiri memiliki keuntungan bahwa Anda dapat memiliki nama yang lebih jelas menunjukkan maksudnya.

Joachim Sauer
sumber
5
Apakah akan ada antarmuka bawaan untuk digunakan, atau haruskah saya membuat antarmuka untuk setiap lambda yang ingin saya ambil?
Marius
Kompromi yang baik untuk dilema usabilitas vs deskriptif nama adalah untuk memperluas antarmuka built in tanpa mengesampingkan metode yang ditentukan. Itu memberi Anda nama deskriptif dengan hanya satu baris kode tambahan.
Will Byrne
Saya tidak mengerti. Dia bisa melewati lambda untuk apa saja dan itu akan berhasil? Apa yang terjadi jika ia melewati (int a, int b, int c)untuk TwoArgIntOperator. Apa yang terjadi jika TwoArgIntOperatormemiliki dua metode dengan tanda tangan yang sama. Jawaban ini membingungkan.
Tomáš Zato - Reinstate Monica
7
@ TomášZato: jika Anda menggunakan lambda dengan argumen yang tidak cocok, kompiler akan mengeluh. Dan antarmuka dengan dua metode (non-default) tidak akan dapat digunakan sebagai lambdas, karena hanya antarmuka fungsional yang dapat digunakan.
Joachim Sauer
Sebuah pertanyaan mendasar: Saya pikir saya masih tidak mengerti aspek melewati suatu metode sebagai parameter dengan parameter dari metode itu hilang. Jika dia menggunakan TwoArgIntOperator sebagai parameter dan dia perlu melewatkan parameter metode itu secara terpisah, bukankah itu terlihat jelek? Apakah ada cara untuk lulus tubuh eksekusi lengkap bersama dengan parameter? Seperti pada contoh Anda, cara untuk menghindari hardcoding "5" dan "10".
instanceOfObject
63

Untuk menggunakan ekspresi Lambda Anda perlu membuat antarmuka fungsional Anda sendiri atau menggunakan antarmuka fungsional Java untuk operasi yang membutuhkan dua integer dan kembali sebagai nilai. IntBinaryOperator

Menggunakan antarmuka fungsional yang ditentukan pengguna

interface TwoArgInterface {

    public int operation(int a, int b);
}

public class MyClass {

    public static void main(String javalatte[]) {
        // this is lambda expression
        TwoArgInterface plusOperation = (a, b) -> a + b;
        System.out.println("Sum of 10,34 : " + plusOperation.operation(10, 34));

    }
}

Menggunakan antarmuka fungsional Java

import java.util.function.IntBinaryOperator;

public class MyClass1 {

    static void main(String javalatte[]) {
        // this is lambda expression
        IntBinaryOperator plusOperation = (a, b) -> a + b;
        System.out.println("Sum of 10,34 : " + plusOperation.applyAsInt(10, 34));

    }
}

Contoh lain yang saya buat ada di sini

pardeep131085
sumber
Tautan ke IntBinaryOperatordokumentasi sudah mati.
Hendrikto
1
ini adalah contoh terbaik lambda's yang saya temukan sejauh ini, dan itu satu-satunya yang benar-benar membuat saya 'mengerti' akhirnya.
JimmySmithJR
1
Sooooooo ... pada dasarnya delegasi, tapi kita tidak seharusnya menyebutnya begitu?
Ryan Lundy
37

Untuk fungsi yang tidak memiliki lebih dari 2 parameter, Anda dapat meneruskannya tanpa mendefinisikan antarmuka Anda sendiri. Sebagai contoh,

class Klass {
  static List<String> foo(Integer a, String b) { ... }
}

class MyClass{

  static List<String> method(BiFunction<Integer, String, List<String>> fn){
    return fn.apply(5, "FooBar");
  }
}

List<String> lStr = MyClass.method((a, b) -> Klass.foo((Integer) a, (String) b));

Di BiFunction<Integer, String, List<String>>, Integerdan Stringparameternya, danList<String> merupakan tipe pengembaliannya.

Untuk fungsi dengan hanya satu parameter, Anda dapat menggunakan Function<T, R>, di mana Tjenis parameternya, dan Rmerupakan jenis nilai pengembaliannya. Lihat halaman ini untuk semua antarmuka yang sudah disediakan oleh Java.

David Wu
sumber
15

Ada versi yang dapat diakses publik dari Java 8 JavaDocs yang diaktifkan Lambda, ditautkan dari http://lambdafaq.org/lambda-resources . (Ini jelas merupakan komentar atas jawaban Joachim Sauer, tetapi saya tidak dapat masuk ke akun SO saya dengan poin reputasi yang perlu saya tambahkan komentar.) Situs lambdafaq (saya pertahankan) menjawab ini dan banyak Jawa lainnya pertanyaan -lambda.

NB Jawaban ini ditulis sebelum dokumentasi Java 8 GA tersedia untuk umum . Tapi saya tetap di tempat, karena FAQ Lambda mungkin masih bermanfaat bagi orang yang mempelajari tentang fitur yang diperkenalkan di Java 8.

Maurice Naftalin
sumber
2
Terima kasih atas tautannya dan fakta bahwa Anda memelihara situs itu! Saya mengambil kebebasan untuk menambahkan tautan ke JavaDoc publik Anda ke jawaban saya.
Joachim Sauer
1
Sebagai catatan: Sepertinya Anda membangun untuk Lambdas apa yang telah dibangun Angelika Langer untuk Generics . Terima kasih untuk itu, Java membutuhkan sumber daya seperti itu!
Joachim Sauer
1
Meskipun tautan ini dapat menjawab pertanyaan, lebih baik untuk memasukkan bagian-bagian penting dari jawaban di sini dan memberikan tautan untuk referensi. Jawaban hanya tautan dapat menjadi tidak valid jika halaman tertaut berubah. - Dari Ulasan
ClearLogic
@ClearLogic Ya, setuju. AFAIR Saya tidak ingin menambahkan apa pun ke jawaban yang ada tetapi hanya untuk menunjukkan di mana saya telah memposting salinan dokumentasi API, yang pada saat itu tidak mudah diakses.
Maurice Naftalin
7

Bagi saya, solusi yang paling masuk akal adalah mendefinisikan Callbackantarmuka:

interface Callback {
    void call();
}

dan kemudian menggunakannya sebagai parameter dalam fungsi yang ingin Anda panggil:

void somewhereInYourCode() {
    method(() -> {
        // You've passed a lambda!
        // method() is done, do whatever you want here.
    });
}

void method(Callback callback) {
    // Do what you have to do
    // ...

    // Don't forget to notify the caller once you're done
    callback.call();
}

Hanya presisi saja

Lambda bukanlah antarmuka khusus, kelas atau apa pun yang Anda dapat mendeklarasikan sendiri. Lambdahanya nama yang diberikan ke () -> {}sintaks khusus, yang memungkinkan keterbacaan yang lebih baik ketika melewati antarmuka metode tunggal sebagai parameter. Itu dirancang untuk menggantikan ini:

method(new Callback() {
    @Override
    public void call() {
        // Classic interface implementation, lot of useless boilerplate code.
        // method() is done, do whatever you want here.
    }
});

Jadi dalam contoh di atas, Callbackadalah tidak lambda, itu hanya antarmuka biasa; lambdaadalah nama sintaks pintasan yang dapat Anda gunakan untuk mengimplementasikannya.

cacat
sumber
6

Bagi siapa pun yang mencari Google ini, metode yang baik adalah menggunakan java.util.function.BiConsumer. ex:

Import java.util.function.Consumer
public Class Main {
    public static void runLambda(BiConsumer<Integer, Integer> lambda) {
        lambda.accept(102, 54)
    }

    public static void main(String[] args) {
        runLambda((int1, int2) -> System.out.println(int1 + " + " + int2 + " = " + (int1 + int2)));
    }

Hasil cetakannya adalah: 166

Big_Bad_E
sumber
1
Alih-alih Consumer<Pair<A,B>>, gunakan BiConsumer<A,B>untuk kasus ini. ( docs )
nobar
Tidak tahu itu ada, saya harus menyaring paket fungsi waktu berikutnya.
Big_Bad_E
5

Ekspresi Lambda dapat dilewatkan sebagai argumen. Untuk meneruskan ekspresi lambda sebagai argumen, tipe parameter (yang menerima ekspresi lambda sebagai argumen) harus dari tipe antarmuka fungsional.

Jika ada antarmuka fungsional -

interface IMyFunc {
   boolean test(int num);
}

Dan ada metode filter yang menambahkan int dalam daftar hanya jika lebih besar dari 5. Perhatikan di sini bahwa metode filter memiliki antarmuka funtional IMyFunc sebagai salah satu parameter. Dalam kasus itu, ekspresi lambda dapat dilewatkan sebagai argumen untuk parameter metode.

public class LambdaDemo {
    public static List<Integer> filter(IMyFunc testNum, List<Integer> listItems) {
        List<Integer> result = new ArrayList<Integer>();
        for(Integer item: listItems) {
            if(testNum.test(item)) {
                result.add(item);
            }
        }
        return result;
    }
    public static void main(String[] args) {
        List<Integer> myList = new ArrayList<Integer>();
        myList.add(1);
        myList.add(4);
        myList.add(6);
        myList.add(7);
        // calling filter method with a lambda expression
        // as one of the param
        Collection<Integer> values = filter(n -> n > 5, myList);

        System.out.println("Filtered values " + values);
    }
}
infoj
sumber
2

Anda dapat menggunakan antarmuka fungsional seperti yang disebutkan di atas. di bawah ini adalah beberapa contohnya

Function<Integer, Integer> f1 = num->(num*2+1);
System.out.println(f1.apply(10));

Predicate<Integer> f2= num->(num > 10);
System.out.println(f2.test(10));
System.out.println(f2.test(11));

Supplier<Integer> f3= ()-> 100;
System.out.println(f3.get());

Semoga ini bisa membantu

pergelangan kaki
sumber
1

Yah, itu mudah. Tujuan dari ekspresi lambda adalah untuk mengimplementasikan Antarmuka Fungsional. Ini adalah antarmuka dengan hanya satu metode.Berikut adalah artikel awesone tentang antarmuka fungsional yang telah ditentukan dan sebelumnya.

Bagaimanapun, jika Anda ingin mengimplementasikan antarmuka fungsional Anda sendiri, buatlah. Sebagai contoh sederhana:

public interface MyFunctionalInterface {
    String makeIt(String s);
}

Jadi mari kita buat kelas, di mana kita akan membuat metode, yang menerima jenis MyFunctionalInterface :

public class Main {

    static void printIt(String s, MyFunctionalInterface f) {
        System.out.println(f.makeIt(s));
    }

    public static void main(String[] args) {

    }
}

Hal terakhir yang harus Anda lakukan adalah meneruskan implementasi MyFunctionalInterface ke metode yang telah kami tentukan:

public class Main {

    static void printIt(String s, MyFunctionalInterface f) {
        System.out.println(f.makeIt(s));
    }

    public static void main(String[] args) {
        printIt("Java", s -> s + " is Awesome");
    }
}

Itu dia!

SanchelliosProg
sumber
1

Lambda bukan objek tetapi Antarmuka Fungsional. Satu dapat mendefinisikan sebanyak Antarmuka Fungsional karena mereka dapat menggunakan @FuntionalInterface sebagai anotasi

@FuntionalInterface
public interface SumLambdaExpression {
     public int do(int a, int b);
}

public class MyClass {
     public static void main(String [] args) {
          SumLambdaExpression s = (a,b)->a+b;
          lambdaArgFunction(s);
     }

     public static void lambdaArgFunction(SumLambdaExpression s) {
          System.out.println("Output : "+s.do(2,5));
     }
}

Outputnya adalah sebagai berikut

Output : 7

Konsep dasar dari Ekspresi Lambda adalah untuk mendefinisikan logika Anda sendiri tetapi Argumen yang sudah didefinisikan. Jadi dalam kode di atas Anda dapat mengubah definisi fungsi do dari penambahan ke definisi lain, tetapi argumen Anda terbatas pada 2.

raja emani
sumber
1

Pada dasarnya untuk meneruskan ekspresi lamda sebagai parameter, kita memerlukan tipe yang bisa kita gunakan. Sama seperti nilai integer yang kami pegang dalam kelas primitif int atau Integer. Java tidak memiliki tipe terpisah untuk ekspresi lamda melainkan menggunakan antarmuka sebagai tipe untuk menahan argumen. Tetapi antarmuka itu harus merupakan antarmuka fungsional .

Vamshi
sumber
0

Lakukan yang berikut ..

Anda telah mendeklarasikan. method(lambda l) Yang ingin Anda lakukan hanyalah membuat Interface dengan nama lambdadan mendeklarasikan satu metode abstrak

public int add(int a,int b);  

nama metode tidak masalah di sini ..

Jadi ketika Anda memanggil MyClass.method( (a,b)->a+b) implementasi ini (a,b)->a+bakan disuntikkan ke antarmuka Anda menambahkan metode. Jadi setiap kali Anda menyebutnya l.addakan mengambil implementasi ini dan melakukan penambahan adan b dan return l.add(2,3)akan kembali 5. - Pada dasarnya inilah yang dilakukan lambda ..

Arasn
sumber
-1

Berikut kira-kira bagaimana C # menangani masalah ini (tetapi dinyatakan sebagai kode Java). Sesuatu seperti ini dapat menangani hampir semua kebutuhan Anda:

import static org.util.function.Functions.*;

public class Test {

    public static void main(String[] args)
    {
        Test.invoke((a, b) -> a + b);       
    }

    public static void invoke(Func2<Integer, Integer, Integer> func)
    {
        System.out.println(func.apply(5, 6));
    }
}

package org.util.function;

public interface Functions {

    //Actions:
    public interface Action {
        public void apply();
    }

    public interface Action1<T1> {
        public void apply(T1 arg1);
    }

    public interface Action2<T1, T2> {
        public void apply(T1 arg1, T2 arg2);
    }

    public interface Action3<T1, T2, T3> {
        public void apply(T1 arg1, T2 arg2, T3 arg3);
    }

    public interface Action4<T1, T2, T3, T4> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    }

    public interface Action5<T1, T2, T3, T4, T5> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
    }

    public interface Action6<T1, T2, T3, T4, T5, T6> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
    }

    public interface Action7<T1, T2, T3, T4, T5, T6, T7> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
    }

    public interface Action8<T1, T2, T3, T4, T5, T6, T7, T8> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
    }

    //Functions:
    public interface Func<TResult> {
        public TResult apply();
    }

    public interface Func1<T1, TResult> {
        public TResult apply(T1 arg1);
    }

    public interface Func2<T1, T2, TResult> {
        public TResult apply(T1 arg1, T2 arg2);
    }

    public interface Func3<T1, T2, T3, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3);
    }

    public interface Func4<T1, T2, T3, T4, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    }

    public interface Func5<T1, T2, T3, T4, T5, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
    }

    public interface Func6<T1, T2, T3, T4, T5, T6, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
    }

    public interface Func7<T1, T2, T3, T4, T5, T6, T7, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
    }

    public interface Func8<T1, T2, T3, T4, T5, T6, T7, T8, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
    }
}
craigrs84
sumber
-2

Ada fleksibilitas dalam menggunakan lambda sebagai parameter. Ini memungkinkan pemrograman fungsional dalam java. Sintaks dasarnya adalah

param -> method_body

Berikut ini caranya, Anda dapat mendefinisikan metode yang menggunakan antarmuka fungsional (lambda digunakan) sebagai parameter. Sebuah. jika Anda ingin mendefinisikan metode yang dideklarasikan di dalam antarmuka fungsional, katakanlah, the antarmuka fungsional diberikan sebagai argumen / parameter ke metode yang dipanggil darimain()

@FunctionalInterface
interface FInterface{
    int callMeLambda(String temp);
}


class ConcreteClass{

    void funcUsesAnonymousOrLambda(FInterface fi){
        System.out.println("===Executing method arg instantiated with Lambda==="));
    }

    public static void main(){
        // calls a method having FInterface as an argument.
        funcUsesAnonymousOrLambda(new FInterface() {

            int callMeLambda(String temp){ //define callMeLambda(){} here..
                return 0;
            }
        }
    }

/***********Can be replaced by Lambda below*********/
        funcUsesAnonymousOrLambda( (x) -> {
            return 0; //(1)
        }

    }

FInterface fi = (x) -> {return 0; };

funcUsesAnonymousOrLambda (fi);

Di sini di atas dapat dilihat, bagaimana ekspresi lambda dapat diganti dengan antarmuka.

Di atas menjelaskan penggunaan ekspresi lambda tertentu, ada lebih banyak. ref Java 8 lambda dalam lambda tidak dapat mengubah variabel dari lambda luar

Hai, India
sumber