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