Um java lambda pode ter mais de 1 parâmetro?

158

Em Java, é possível que um lambda aceite vários tipos diferentes?

Ou seja: Variável única funciona:

    Function <Integer, Integer> adder = i -> i + 1;
    System.out.println (adder.apply (10));

Varargs também funciona:

    Function <Integer [], Integer> multiAdder = ints -> {
        int sum = 0;
        for (Integer i : ints) {
            sum += i;
        }
        return sum;
    };

    //.... 
    System.out.println ((multiAdder.apply (new Integer [] { 1, 2, 3, 4 })));

Mas eu quero algo que possa aceitar muitos tipos diferentes de argumentos, por exemplo:

    Function <String, Integer, Double, Person, String> myLambda = a , b, c, d->  {
    [DO STUFF]
    return "done stuff"
    };

O uso principal é ter pequenas funções embutidas dentro das funções por conveniência.

Eu olhei em volta do google e inspecionei o pacote de funções do Java, mas não consegui encontrar. Isso é possível?

Leo Ufimtsev
fonte

Respostas:

178

É possível se você definir uma interface funcional com vários parâmetros de tipo. Não existe esse tipo incorporado. (Existem alguns tipos limitados com vários parâmetros.)

@FunctionalInterface
interface Function6<One, Two, Three, Four, Five, Six> {
    public Six apply(One one, Two two, Three three, Four four, Five five);
}

public static void main(String[] args) throws Exception {
    Function6<String, Integer, Double, Void, List<Float>, Character> func = (a, b, c, d, e) -> 'z';
}

Eu chamei Function6aqui. O nome fica a seu critério, apenas tente não colidir com os nomes existentes nas bibliotecas Java.


Também não há como definir um número variável de parâmetros de tipo, se é sobre isso que você estava perguntando.


Alguns idiomas, como o Scala, definem vários tipos construídos nesses tipos, com 1, 2, 3, 4, 5, 6, etc. parâmetros de tipo.

Sotirios Delimanolis
fonte
58
Você sempre pode usar o curry:Function<One, Function<Two, Function<Three, Function<Four, Function<Five, Six>>>>> func = a -> b -> c -> d -> e -> 'z';
Holger
@SotiriosDelimanolis: Prefiro escolher um nome diferente do Functionqual os confrontos com java.util.function.Function<T,R>isso podem não ser claros para os iniciantes.
Nikolas
@ Nikolas Isso é razoável. Editado.
Sotirios Delimanolis
39

Para algo com 2 parâmetros, você pode usar BiFunction. Se precisar de mais, você pode definir sua própria interface de funções, assim:

@FunctionalInterface
public interface FourParameterFunction<T, U, V, W, R> {
    public R apply(T t, U u, V v, W w);
}

Se houver mais de um parâmetro, você precisará colocar parênteses em torno da lista de argumentos, assim:

FourParameterFunction<String, Integer, Double, Person, String> myLambda = (a, b, c, d) -> {
    // do something
    return "done something";
};
tbodt
fonte
35

Nesse caso, você pode usar interfaces da biblioteca padrão (java 1.8):

java.util.function.BiConsumer
java.util.function.BiFunction

Há um pequeno (não o melhor) exemplo de método padrão na interface:

default BiFunction<File, String, String> getFolderFileReader() {
    return (directory, fileName) -> {
        try {
            return FileUtils.readFile(directory, fileName);
        } catch (IOException e) {
            LOG.error("Unable to read file {} in {}.", fileName, directory.getAbsolutePath(), e);
        }
        return "";
    };
}}
ayurchuk
fonte
5
Você receberá mais votos dos fãs do Java8 se alterar sua pergunta para ilustrar como essas interfaces podem ser usadas para satisfazer o requisito.
Martin Cowie
3
BiFunction permite definir apenas funções de dois argumentos, a pergunta é sobre funções com qualquer número de argumentos
Dmitry Klochkov
8

Para fazer uso do lambda: Existem três tipos de operação:
1. Aceite o parâmetro -> Consumidor
2. Teste o parâmetro return boolean -> Predicate
3. Manipule o parâmetro e retorne o valor -> Function

Interface funcional Java até dois parâmetros:
Interface de parâmetro único Função de predicado do
consumidor Interface de dois parâmetros BiConsumer BiPredicate BiFunction







Por mais de dois , você deve criar uma interface funcional da seguinte maneira (tipo Consumidor):

@FunctionalInterface
public interface FiveParameterConsumer<T, U, V, W, X> {
    public void accept(T t, U u, V v, W w, X x);
}
amoljdv06
fonte
1

Outra alternativa, não tenho certeza se isso se aplica ao seu problema específico, mas a alguns pode ser aplicável, é usar UnaryOperatorna biblioteca java.util.function. onde ele retorna o mesmo tipo que você especificar, então você coloca todas as suas variáveis ​​em uma classe e é como um parâmetro:

public class FunctionsLibraryUse {

    public static void main(String[] args){
        UnaryOperator<People> personsBirthday = (p) ->{
            System.out.println("it's " + p.getName() + " birthday!");
            p.setAge(p.getAge() + 1);
            return p;
        };
        People mel = new People();
        mel.setName("mel");
        mel.setAge(27);
        mel = personsBirthday.apply(mel);
        System.out.println("he is now : " + mel.getAge());

    }
}
class People{
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

Portanto, a classe que você possui, nesse caso Person, pode ter inúmeras variáveis ​​de instância e não precisará alterar o parâmetro da sua expressão lambda.

Para os interessados, escrevi notas sobre como usar a biblioteca java.util.function: http://sysdotoutdotprint.com/index.php/2017/04/28/java-util-function-library/

mel3kings
fonte
1

Você também pode usar a biblioteca jOOL - https://github.com/jOOQ/jOOL

Já preparou interfaces de função com diferentes números de parâmetros. Por exemplo, você pode usar org.jooq.lambda.function.Function3etc, de Function0até Function16.

PetroCliff
fonte