Como defino um método que usa um lambda como parâmetro no Java 8?

363

No Java 8, os métodos podem ser criados como expressões Lambda e podem ser passados ​​por referência (com um pouco de trabalho oculto). Existem muitos exemplos online, com lambdas sendo criadas e usadas com métodos, mas não há exemplos de como criar um método usando um lambda como parâmetro. Qual é a sintaxe para isso?

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


class MyClass{
  //How do I define this method?
  static int method(Lambda l){
    return l(5, 10);
  }
}
Marius
fonte
29
Boa pergunta. E você está certo: nenhum dos tutoriais contém essa parte.
Martin

Respostas:

247

Lambdas são puramente uma construção de site de chamada: o destinatário do lambda não precisa saber que um Lambda está envolvido; em vez disso, ele aceita uma interface com o método apropriado.

Em outras palavras, você define ou usa uma interface funcional (ou seja, uma interface com um único método) que aceita e retorna exatamente o que deseja.

Para isso, o Java 8 vem com um conjunto de tipos de interface comumente usados java.util.function(obrigado a Maurice Naftalin pela dica sobre o JavaDoc).

Para este caso de uso específico não java.util.function.IntBinaryOperatorcom um único int applyAsInt(int left, int right)método , assim que você pode escrever seu methodparecido com isto:

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

Mas você também pode definir sua própria interface e usá-la assim:

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

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

O uso de sua própria interface tem a vantagem de poder ter nomes que indicam mais claramente a intenção.

Joachim Sauer
fonte
5
Haverá interfaces internas a serem usadas ou devo criar uma interface para cada lambda que eu desejar?
Marius
Um bom compromisso com o dilema de reusabilidade versus nome descritivo seria estender a interface interna sem substituir o método especificado. Isso fornece seu nome descritivo com apenas uma única linha de código adicional.
Will Byrne
Eu não entendo. Ele pode passar lambda por qualquer coisa e vai funcionar? O que acontece se ele passa (int a, int b, int c)para TwoArgIntOperator. O que acontece se TwoArgIntOperatortiver dois métodos com a mesma assinatura. Esta resposta é confusa.
Tomáš Zato - Restabelece Monica
7
@ TomášZato: se você usar um lambda com argumentos não correspondentes, o compilador irá reclamar. E interfaces com dois métodos (não padrão) não serão utilizáveis ​​como lambdas, pois somente interfaces funcionais podem ser usadas.
Joachim Sauer
Uma pergunta básica: acho que ainda não entendo o aspecto de passar um método como um parâmetro com os parâmetros desse método ausentes. Se ele está passando o TwoArgIntOperator como um parâmetro e ele precisa passar o parâmetro desse método separadamente, não parece feio? Existe uma maneira de passar o corpo de execução completo junto com o parâmetro? Como no seu exemplo, uma maneira de evitar a codificação "5" e "10".
instanceOfObject
63

Para usar a expressão Lambda, você precisa criar sua própria interface funcional ou usar a interface funcional Java para operação que requer dois números inteiros e retorna como valor. IntBinaryOperator

Usando interface funcional definida pelo usuário

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

    }
}

Usando interface funcional 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));

    }
}

Outro exemplo que eu criei está aqui

pardeep131085
fonte
O link para a IntBinaryOperatordocumentação está morto.
Hendrikto
11
este é o melhor exemplo de lambda que eu encontrei até agora e foi o único que realmente me fez 'pegá-lo' finalmente.
JimmySmithJR
11
Tããããããão basicamente um delegado, mas não devemos chamá-lo assim?
Ryan Lundy
37

Para funções que não possuem mais de 2 parâmetros, você pode transmiti-las sem definir sua própria interface. Por exemplo,

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

In BiFunction<Integer, String, List<String>>, Integere Stringsão seus parâmetros e List<String>é seu tipo de retorno.

Para uma função com apenas um parâmetro, você pode usar Function<T, R>, onde Té o tipo de parâmetro e Ro tipo de valor de retorno. Consulte esta página para todas as interfaces que já são disponibilizadas pelo Java.

David Wu
fonte
15

Há uma versão pública acessível pela Web dos JavaDocs Java 8 habilitados para Lambda, vinculados em http://lambdafaq.org/lambda-resources . (Obviamente, isso deve ser um comentário sobre a resposta de Joachim Sauer, mas não consigo entrar na minha conta SO com os pontos de reputação que preciso adicionar um comentário.) O site lambdafaq (eu o mantenho) responde a esse e muitos outros Java perguntas -ambda.

Nota: esta resposta foi escrita antes da documentação do Java 8 GA ser disponibilizada ao público . Eu deixei o local, porém, porque as Perguntas frequentes sobre o Lambda ainda podem ser úteis para as pessoas que aprendem sobre os recursos introduzidos no Java 8.

Maurice Naftalin
fonte
2
Obrigado pelo link e pelo fato de você manter esse site! Tomei a liberdade de adicionar links ao seu JavaDoc público à minha resposta.
Joachim Sauer
11
Como uma observação lateral: parece que você está construindo para a Lambdas o que Angelika Langer construiu para a Generics . Obrigado por isso, o Java precisa desses recursos!
Joachim Sauer
11
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada. - Da avaliação
ClearLogic
@ClearLogic Sim, concordou. AFAIR Eu não queria adicionar nada às respostas existentes, mas apenas para apontar onde havia postado uma cópia da documentação da API, que na época não era facilmente acessível.
Maurice Naftalin
7

Para mim, a solução que faz mais sentido é definir uma Callbackinterface:

interface Callback {
    void call();
}

e depois usá-lo como parâmetro na função que você deseja chamar:

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

Apenas uma precisão

Um lambda não é uma interface especial, classe ou qualquer outra coisa que você possa declarar por si mesmo. Lambdaé apenas o nome dado à () -> {}sintaxe especial, que permite melhor legibilidade ao passar interfaces de método único como parâmetro. Foi projetado para substituir isso:

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

Portanto, no exemplo acima, nãoCallback é um lambda, é apenas uma interface regular; é o nome da sintaxe de atalho que você pode usar para implementá-lo.lambda

flawyte
fonte
6

Para quem está pesquisando no Google, um bom método seria usar 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)));
    }

A impressão seria: 166

Big_Bad_E
fonte
11
Em vez de Consumer<Pair<A,B>>, use BiConsumer<A,B>para este caso. ( docs )
nobar 27/09/18
Não sabia que existia, eu deveria examinar o pacote de funções na próxima vez.
Big_Bad_E
5

A expressão lambda pode ser passada como argumento.Para passar uma expressão lambda como argumento, o tipo do parâmetro (que recebe a expressão lambda como argumento) deve ser do tipo de interface funcional.

Se houver uma interface funcional -

interface IMyFunc {
   boolean test(int num);
}

E existe um método de filtro que adiciona o int na lista somente se for maior que 5. Observe aqui que o método de filtro possui a interface funcional IMyFunc como um dos parâmetros. Nesse caso, a expressão lambda pode ser transmitida como um argumento para o parâmetro do método

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
fonte
2

Você pode usar interfaces funcionais como mencionado acima. abaixo estão alguns dos exemplos

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

Espero que ajude

ank
fonte
1

Bem, isso é fácil. O objetivo da expressão lambda é implementar a Interface Funcional. É a interface com apenas um método.Aqui está um artigo impressionante sobre interfaces funcionais predefinidas e herdadas.

De qualquer forma, se você deseja implementar sua própria interface funcional, faça-a. Apenas para um exemplo simples:

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

Então, vamos criar uma classe, onde criaremos um método, que aceita o tipo de MyFunctionalInterface :

public class Main {

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

    public static void main(String[] args) {

    }
}

A última coisa que você deve fazer é passar a implementação do MyFunctionalInterface para o método que definimos:

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

É isso aí!

SanchelliosProg
fonte
1

O Lambda não é um objeto, mas uma Interface Funcional. Pode-se definir o máximo de interfaces funcionais usando o @FuntionalInterface como uma anotação

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

A saída será a seguinte

Output : 7

O conceito básico de uma expressão lambda é definir sua própria lógica, mas os argumentos já definidos. Portanto, no código acima, você pode alterar a definição da função do para outra definição, mas seus argumentos são limitados a 2.

raja emani
fonte
1

Basicamente, para passar uma expressão lamda como parâmetro, precisamos de um tipo no qual possamos mantê-la. Assim como um valor inteiro, mantemos a classe primitiva int ou Integer. Java não tem um tipo separado para expressão lamda, em vez disso, usa uma interface como o tipo para conter o argumento. Mas essa interface deve ser uma interface funcional .

Vamshi
fonte
0

Faça o seguinte ..

Você declarou method(lambda l) Tudo que deseja fazer é criar uma interface com o nome lambdae declarar um método abstrato

public int add(int a,int b);  

nome do método não importa aqui ..

Então, quando u chamar MyClass.method( (a,b)->a+b) Esta implementação (a,b)->a+bserá injetado para o seu método de interface add .Assim sempre que você chamar l.addele vai levar essa implementação e realizar adição de ae b e return l.add(2,3)irá retornar 5. - Basicamente é isso que o lambda faz ..

Arasn
fonte
-1

Aqui está mais ou menos como o C # lida com esse problema (mas expresso como código Java). Algo assim poderia lidar com quase todas as suas necessidades:

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
fonte
-2

Há flexibilidade no uso de lambda como parâmetro. Permite programação funcional em java. A sintaxe básica é

param -> method_body

A seguir, é possível definir um método usando a interface funcional (lambda é usado) como parâmetro. uma. se você deseja definir um método declarado dentro de uma interface funcional, por exemplo, a interface funcional é fornecida como argumento / parâmetro para um método chamado frommain()

@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) -> {retorno 0; };

funcUsesAnonymousOrLambda (fi);

Aqui acima, pode ser visto como uma expressão lambda pode ser substituída por uma interface.

Acima explica um uso específico da expressão lambda, há mais. ref O Java 8 lambda dentro de um lambda não pode modificar a variável do lambda externo

hi.nitish
fonte