Czy java lambda może mieć więcej niż 1 parametr?

157

Czy w Javie możliwe jest, aby lambda akceptowała wiele różnych typów?

To znaczy: pojedyncza zmienna działa:

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

Varargs również działa:

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

Ale chcę czegoś, co akceptuje wiele różnych typów argumentów, np .:

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

Głównym zastosowaniem są małe funkcje wbudowane w funkcje dla wygody.

Rozejrzałem się po google i sprawdziłem pakiet funkcji Java, ale nie mogłem znaleźć. czy to możliwe?

Leo Ufimtsev
źródło

Odpowiedzi:

178

Jest to możliwe, jeśli zdefiniujesz taki funkcjonalny interfejs z wieloma parametrami typu. Nie ma takiego wbudowanego typu. (Istnieje kilka ograniczonych typów z wieloma parametrami).

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

Nazwałem to Function6tutaj. Nazwa jest według własnego uznania, po prostu staraj się nie kolidować z istniejącymi nazwami w bibliotekach Java.


Nie ma również możliwości zdefiniowania zmiennej liczby parametrów typu, jeśli o to pytałeś.


Niektóre języki, takie jak Scala, definiują szereg wbudowanych w tego typu typy, z parametrami typu 1, 2, 3, 4, 5, 6 itd.

Sotirios Delimanolis
źródło
58
Zawsze możesz użyć Currying:Function<One, Function<Two, Function<Three, Function<Four, Function<Five, Six>>>>> func = a -> b -> c -> d -> e -> 'z';
Holger
@SotiriosDelimanolis: Wolałbym wybrać inną nazwę, z Functionktórej koliduje, java.util.function.Function<T,R>która może być niejasna dla początkujących.
Nikolas
@Nikolas To rozsądne. Edytowano.
Sotirios Delimanolis
39

W przypadku czegoś z 2 parametrami możesz użyć BiFunction. Jeśli potrzebujesz więcej, możesz zdefiniować własny interfejs funkcji, na przykład:

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

Jeśli jest więcej niż jeden parametr, należy umieścić listę argumentów w nawiasach, na przykład:

FourParameterFunction<String, Integer, Double, Person, String> myLambda = (a, b, c, d) -> {
    // do something
    return "done something";
};
tbodt
źródło
34

W tym przypadku możesz użyć interfejsów z domyślnej biblioteki (java 1.8):

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

Jest mały (nie najlepszy) przykład domyślnej metody w interfejsie:

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
źródło
5
Otrzymasz więcej głosów pozytywnych od fanów Java8, jeśli poprawisz swoje pytanie, aby zilustrować, w jaki sposób te interfejsy mogą być wykorzystane do spełnienia wymagań.
Martin Cowie
3
BiFunction pozwala zdefiniować tylko funkcje dwuargumentowe, pytanie dotyczy funkcji z dowolną liczbą argumentów
Dmitry Klochkov
8

Aby skorzystać z lambda: Istnieją trzy typy operacji:
1. Zaakceptuj parametr -> Konsument
2. Przetestuj zwrot parametru boolean -> Predykat
3. Manipuluj parametrem i wartością zwracaną -> Funkcja

Interfejs funkcjonalny Java do dwóch parametrów:
Interfejs jednoparametrowy Predykat
konsumenta Funkcja Interfejs dwuparametrowy BiConsumer BiPredicate BiFunction







W przypadku więcej niż dwóch osób musisz utworzyć funkcjonalny interfejs w następujący sposób (typ konsumenta):

@FunctionalInterface
public interface FiveParameterConsumer<T, U, V, W, X> {
    public void accept(T t, U u, V v, W w, X x);
}
amoljdv06
źródło
1

Inną alternatywą, nie jestem pewien, czy dotyczy to twojego konkretnego problemu, ale w niektórych może mieć zastosowanie, jest użycie UnaryOperatorbiblioteki java.util.function. gdzie zwraca ten sam typ, który podałeś, więc umieszczasz wszystkie zmienne w jednej klasie i czy jest to parametr:

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

W tym przypadku klasa, którą masz, Personmoże mieć wiele zmiennych instancji i nie będzie musiała zmieniać parametru wyrażenia lambda.

Dla zainteresowanych napisałem notatki na temat korzystania z biblioteki java.util.function: http://sysdotoutdotprint.com/index.php/2017/04/28/java-util-function-library/

mel3kings
źródło
1

Możesz również skorzystać z biblioteki jOOL - https://github.com/jOOQ/jOOL

Posiada już przygotowane interfejsy funkcyjne z różną liczbą parametrów. Na przykład możesz użyć org.jooq.lambda.function.Function3, etc od Function0do Function16.

PetroCliff
źródło