[JDK durmiente] Explicación detallada de la interfaz de programación funcional de Java Consumidor, Función

Hoy presentaremos las otras dos interfaces de programación de funciones de Java, Consumidor y Función. ¿Qué hacen estas dos funciones? Primero echemos un vistazo a la definición oficial:

  • Consumidor: representa una operación que acepta un único parámetro de entrada pero no devuelve un resultado.
  • Función: Representa una función que acepta un parámetro y genera un resultado.

一、Consumidor

1.1 Código fuente

@FunctionalInterface
public interface Consumer<T> {
    
    
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
    
    
        Objects.requireNonNull(after);
        return (T t) -> {
    
     accept(t); after.accept(t); };
    }
}

1.2 Parece familiar

De hecho, a menudo usamos Consumer, mire el siguiente ejemplo:

List<String> list = Arrays.asList("1", "2", "3");
list.forEach(System.out::println);

La función forEach que usamos a menudo se implementa a través del Consumidor, por lo que es necesario dominar al Consumidor. Echemos un vistazo a la implementación de forEach en ArrayList:

public void forEach(Consumer<? super E> action) {
    
    
    Objects.requireNonNull(action);
    final int expectedModCount = modCount;
    
    final E[] elementData = (E[]) this.elementData;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
    
    
        action.accept(elementData[i]);
    }
    if (modCount != expectedModCount) {
    
    
        throw new ConcurrentModificationException();
    }
}

No es necesario presentarlo, creo que todos pueden entenderlo. El consumidor representa una operación personalizada. Si la operación se pasa como parámetro a otra función, la operación personalizada se puede realizar en la función. El código de bucle for anterior es equivalente a:

for (int i=0; modCount == expectedModCount && i < size; i++) {
    
    
    System.out.println(elementData[i]);
}

1.3 Implementación

Si la operación es común o general, puede usar una clase para implementar Consumer, guardar la operación y usarla rápidamente cuando sea necesario.

// 实体类
public class Person {
    
    
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
    
    
        this.name = name;
        this.age = age;
    }
    //...
}
// 打印小孩
public class PrintChild implements Consumer<Person> {
    
    
    @Override
    public void accept(Person person) {
    
    
        if (person.getAge() < 18) {
    
    
            System.out.println(person.getName() + "还是个孩子啊");
        }
    }
}

// 测试代码
List<Person> list1 = Arrays.asList(
                new Person("大壮", 19),
                new Person("小光", 16),
                new Person("小小", 15));
list1.forEach(new PrintChild());

1.4 Introducción a la función y luego

Código fuente de la función:

default Consumer<T> andThen(Consumer<? super T> after) {
    
    
    Objects.requireNonNull(after);
    return (T t) -> {
    
     accept(t); after.accept(t); };
}

La función de la función andThen es fusionar dos operaciones de Consumidor y devolver un nuevo Consumidor, que se utiliza de la siguiente manera:

// 打印年龄
public class PrintAge implements Consumer<Person> {
    
    
    @Override
    public void accept(Person person) {
    
    
        System.out.println(person.getName() + "的年龄是:" + person.getAge() + "岁");
    }
}

// 测试代码
List<Person> list1 = Arrays.asList(
                new Person("小雨", 19),
                new Person("小光", 16),
                new Person("小小", 15));
list1.forEach(new PrintChild().andThen(new PrintAge()));

// 结果
小雨的年龄是:19岁
小光是个孩子
小光的年龄是:16岁
小小是个孩子
小小的年龄是:15

1.5 Otras variaciones de Consumidor

Nombre de la interfaz parámetro Tipo de devolución describir
BiConsumidor (T, U) vacío BiConsumer acepta dos parámetros
Doble Consumidor doble vacío Acepta un parámetro de tipo doble
Consumidor internacional En t vacío Acepta un parámetro de tipo int
Consumidor largo largo vacío Acepta un parámetro de tipo largo
ObjDoubleConsumer (T, doble) vacío Acepta un tipo de objeto y un parámetro de tipo doble.
ObjIntConsumidor (T, entero) vacío Acepta un tipo de objeto y un parámetro de tipo int
ObjLongConsumidor (T, largo) vacío Acepta un tipo de objeto y un parámetro de tipo largo.

2.Función

Función y Consumidor son funcionalmente idénticos, pero Función devuelve resultados.

2.1 Código fuente

@FunctionalInterface
public interface Function<T, R> {
    
    

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    
    
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    
    
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
    
    
        return t -> t;
    }
}

2.2 Parece familiar

También usamos Function con frecuencia, aquí:

List<Person> list1 = Arrays.asList(
                new Person("小雨", 19),
                new Person("小光", 16),
                new Person("小小", 15));
List<String> list = list1.stream()
    .map(Person::getName)
    .collect(Collectors.toList());

Si ha leído otro artículo escrito por el autor ( Revisitando la programación funcional de Java 8 ), entonces debería poder comprender el siguiente código. De hecho, la operación del mapa anterior se puede restaurar a:

List<String> list = list1.stream()
                .map(person -> {
    
    
                    return person.getName();
                })
                .collect(Collectors.toList());

El mapa encapsula una función con un valor de retorno y la función se pasa al mapa como parámetro.

2.3 Introducción a la función de composición

Código fuente de la función:

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    
    
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}

La función de esta función es combinar dos funciones: el parámetro Función anterior se ejecuta primero y el resultado después de la ejecución se entrega a la función que llama para su ejecución.

// 加法
public class AddFunc implements Function<Integer, Integer> {
    
    
    private int origin;

    public AddFunc(int origin) {
    
    
        this.origin = origin;
    }

    @Override
    public Integer apply(Integer integer) {
    
    
        return this.origin + integer;
    }
}

// 减法
public class ReduceFunc implements Function<Integer, Integer> {
    
    
    private int origin;
    private boolean isMinuend;// origin被减数与否

    public ReduceFunc(int origin, boolean isMinuend) {
    
    
        this.origin = origin;
        this.isMinuend = isMinuend;
    }

    @Override
    public Integer apply(Integer integer) {
    
    
        return isMinuend ? this.origin - integer : integer - this.origin;
    }
}

// 测试代码
public class Test {
    
    
    public static void main(String[] args) {
    
    
        // 计算 1 + (2 - 3)
        System.out.println(handle(new AddFunc(1).compose(new ReduceFunc(2, true)), 3));
    }

    public static int handle(Function<Integer, Integer> function, Integer integer) {
    
    
        return function.apply(integer);
    }
}

2.4 Introducción a la función y luego

Código fuente de la función:

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    
    
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

La función andThen también combina dos funciones, pero se ejecuta después del parámetro Función.

// 测试代码
public class Test {
    
    
    public static void main(String[] args) {
    
    
        // 计算 1 + 2 - 4
        System.out.println(handle(new AddFunc(1).andThen(new ReduceFunc(4, false)), 2));
    }

    public static int handle(Function<Integer, Integer> function, Integer integer) {
    
    
        return function.apply(integer);
    }
}

2.5 función de identidad

Código fuente:

static <T> Function<T, T> identity() {
    
    
    return t -> t;
}

Bueno, la función de esta función es devolver una Función con la misma entrada y salida, entonces, ¿para qué sirve esta función? Ver ejemplo:

// 示例:将list转变为map
List<Person> list = Arrays.asList(
                new Person("xiaoxiao", 11),
                new Person("dazhuang", 15));
Map<String, Person> map = list.stream()
    .collect(Collectors.toMap(Person::getName, person -> person));

// 使用identity函数
List<Person> list = Arrays.asList(
                new Person("xiaoxiao", 11),
                new Person("dazhuang", 15));
Map<String, Person> map = list.stream()
    .collect(Collectors.toMap(Person::getName, Function.identity()));

Ambos métodos de uso son posibles, pero ¿cuál crees que es más encantador (bi) y ge (ge)?

2.6 ¿Qué podemos hacer?

  • Después de dominar la función, al escribir la lógica del componente público, puede descartar parte de la lógica y dejar que la persona que llama implemente sus propias características, lo que puede mejorar la flexibilidad del componente.
  • Implemente la interfaz de función y defina algunas operaciones comunes para reducir la redundancia de código.

La base de la aplicación es dominarlo primero, sólo dominándolo podrás usarlo con habilidad. Esta es una taza de sopa de pollo venenosa. (En silencio BB: El último día del Dragon Boat Festival ha terminado, T . T)

2.7 Otras variantes de Función

Nombre de la interfaz parámetro Tipo de devolución describir
Bifunción (T, U) R BiFunction acepta dos parámetros
Doble Función doble R Acepta un parámetro de tipo doble
Función DoubleToInt doble En t Acepta parámetros de tipo doble y devuelve resultados de tipo int
Función DoubleToLong doble largo Acepta parámetros de tipo doble y devuelve resultados de tipo largo.
Función Int En t R Acepta un parámetro de tipo int
Función IntToDouble En t doble Acepta parámetros de tipo int y devuelve resultados de tipo doble
Función IntToLong En t largo Acepta parámetros de tipo int y devuelve resultados de tipo largo.
Función larga largo R Acepta un parámetro de tipo largo
Función larga a doble largo doble Acepta parámetros de tipo largo y devuelve resultados de tipo doble
Función LongToInt largo En t Acepta parámetros de tipo largo y devuelve resultados de tipo int
Función ToDoubleBi (T, U) doble Acepta dos parámetros y devuelve un resultado de tipo doble.
Para doble función t doble Acepta un tipo de Objeto y devuelve un parámetro de tipo doble
Función ToIntBi (T, U) En t Acepta dos parámetros y devuelve un resultado de tipo int
Función ToInt t En t Acepta un tipo de objeto y devuelve un parámetro de tipo int
Función ToLongBi (T, U) largo Acepta dos parámetros y devuelve un resultado de tipo largo.
Función ToLong t largo Acepta un tipo de objeto y devuelve un parámetro de tipo largo

Recomendación de artículo:

[JDK durmiente] Explicación detallada del predicado de la interfaz de programación funcional de Java

[JDK durmiente] Proveedor de explicación detallada de la interfaz de programación funcional de Java

[JDK dormido] Explicación detallada de la interfaz de programación funcional de Java UnaryOperator, BinaryOperator

fin
El paraíso del desarrollo de Java

Supongo que te gusta

Origin blog.csdn.net/u012534326/article/details/107031007
Recomendado
Clasificación