Java 8: Введите список функциональных интерфейсов и вызовите их динамически после .stream () - PullRequest
4 голосов
/ 18 июня 2020

У меня есть следующий метод:


public void caller(){
    List<Class1> data1 = Arrays.asList(new Class1(), new Class1() ...);
    List<Class2> data2 = Arrays.asList(new Class2(), new Class2() ...);

    // The following is what I'm trying to implement:
    List<BiConsumer<Class1, Double>> peeks1 = Arrays.asList(Class1::setOneNum, Class1::setAnotherNum, Class1:: setMoreNum);
    List<BiConsumer<Class2, Double>> peeks2 = Arrays.asList(Class2::setSomeNum1, Class2::setSomeNum2);

    helper(data1, peeks1);
    helper(data2, peeks2);
    ...
}

private <T> List<T> helper(List<T> data, List<BiConsumer<T, Double>> peeks) {

        for(BiConsumer<T, Double> singlePeek: peeks){
            data = data.stream()
                    .peek(a -> singlePeek.accept(a, math.random()))
                    .collect(Collectors.toList());
        }

        return data;
    }

Существуют и другие реализации, общие для Class1 и Class2, единственная разница - это методы, вызываемые после .stream(), поэтому я ' m пытается «объединить» функции в одну helper.

Где BiConsumer - сеттер. Я хочу вызвать список сеттеров после stream (). Но я не могу ввести список функциональных интерфейсов в helper() (то, что я пробовал, было Arrays.asList(Class1::setNum, Class1::setAnotherNum, Class1::setMoreNum), не будет работать как ввод, поскольку Array.asList() принимает только Object). Так есть ли обходной путь? Спасибо!

@ user7 Спасибо, что указали на это. Я был неосторожен, но исправил "опечатку". И добавил функцию звонящего.

Ответы [ 2 ]

6 голосов
/ 18 июня 2020

Необходимо указать тип цели при вызове метода .asList:

Arrays.<BiConsumer<Object, Double>>asList(Class1::setOneNum, ...)

Обновление:

Согласно обновленному коду вопроса результат Arrays.asList не передается напрямую методу helper, поэтому явная типизация не требуется.

Единственные возможные причины, по которым код не работает:

  1. По крайней мере, один из методов (setOneNum, setSomeNum1, ...) имеет неверные типы параметров

  2. По крайней мере один из методов не является статическим c

2 голосов
/ 19 июня 2020

Могу ли я посоветовать вам попытаться сделать его немного более функциональным?

Для вашего кода рассмотрите следующий помощник, он будет использовать функцию как первоклассную концепцию гражданина и сделает некоторые высокие требования Функции:

    private <T, V> Function<Supplier<T>, Supplier<T>> helper(Supplier<V> v, 
                                                             BiConsumer<T, V> bc) {
        return (Supplier<T> r) -> {
            bc.accept(r.get(), v.get());
            return r;
        };
    }

Эта вспомогательная функция ожидает от Supplier некоторого типа значения и BiConsumer, который будет вашей функцией установки. Возврат - это функция поставщиков того же класса, с которым вы работаете.

Благодаря этому мы можем сделать что-то вроде оператора конвейерной передачи функциональных языков. Их предпосылка заключается в том, что данные должны обрабатываться в конвейерной операции.

List<Class1> data1 = Arrays.asList(new Class1(), new Class1());
List<Class2> data2 = Arrays.asList(new Class2(), new Class2());
Supplier<Double> random = () -> Math.random();

Это будут наши данные, у вас есть тот же массив, а теперь поставщик со случайным значением, которое вы хотите.

Теперь давайте скомпонуем наш конвейер с andThem:

        data1.stream()//
            .forEach(data -> {
                helper(random, Class1::setOneNum)//
                            .andThen(helper(random, Class1::setAnotherNum))//
                            .andThen(helper(random, Class1::setMoreNum))//
                            .apply(() -> data);
                    System.out.println(data.toString());
                });

        data2.stream()//
            .forEach(data -> {
                helper(random, Class2::setSomeNum1)//
                        .andThen(helper(random, Class2::setSomeNum2))//
                        .apply(() -> data);
                System.out.println(data.toString());
            });

Как видите, вспомогательную функцию можно связать вместе с методом andThem интерфейса Function. Это заставит Java выполнить вспомогательную функцию и использовать ее return в качестве параметра следующей функции.

Параметр данных будет "вставлять" значения классов и будет изменяться в каждой цепочке. После итерации все объекты будут

И результат:

  • Class1 [oneNum = 0,047, anotherNum = 0,482, moreNum = 0,339]
  • Class1 [oneNum = 0,131 , anotherNum = 0,889, moreNum = 0,411]
  • Class2 [someNum1 = 0,18, someNum2 = 0,004]
  • Class2 [someNum1 = 0,497, someNum2 = 0,702]

Думаю, это тот же результат, что и вам. И, как видите, вам не нужно передавать какие-либо дженерики, поскольку Java это хорошо понимает.

Классы, которые я сделал для справки:

class Class1 {
double oneNum;
double anotherNum;
double moreNum;

public double getOneNum() {
    return oneNum;
}

public void setOneNum(double oneNum) {
    this.oneNum = oneNum;
}

public double getAnotherNum() {
    return anotherNum;
}

public void setAnotherNum(double anotherNum) {
    this.anotherNum = anotherNum;
}

public double getMoreNum() {
    return moreNum;
}

public void setMoreNum(double moreNum) {
    this.moreNum = moreNum;
}

@Override
public String toString() {
    return MessageFormat.format("Class1 [oneNum={0}, anotherNum={1}, moreNum={2}]", oneNum, anotherNum, moreNum);
}

}

class Class2 {
double someNum1;
double someNum2;

public double getSomeNum1() {
    return someNum1;
}

public void setSomeNum1(double someNum1) {
    this.someNum1 = someNum1;
}

public double getSomeNum2() {
    return someNum2;
}

public void setSomeNum2(double someNum2) {
    this.someNum2 = someNum2;
}

@Override
public String toString() {
    return MessageFormat.format("Class2 [someNum1={0}, someNum2={1}]", someNum1, someNum2);
}

}

...