跳到主要内容

06-java 函数式接口

只有一个抽象方法的接口我们称之为函数接口。JDK的函数式接口都加上了@FunctionalInterface 注解进行标识。但是无论是否加上该注解只要接口中只有一个抽象方法,都是函数式接口。

函数式接口的关键特点是可以被Lambda表达式所实现。函数式接口与Lambda表达式结合使用,可以实现更简洁和可读性强的代码。

jdk 自带的一些常用的一些接口Callable、Runnable、Comparator等在JDK8中都添加了@FunctionalInterface注解。自定义函数式接口时,@FunctionalInterface是可选的,就算不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口 。

使用函数式接口

示例一

这里我们先定义一个带有一个方法的接口

@FunctionalInterface
public interface ShowInterface {
void show();
}

定义一个方法以函数式接口作参数,可以使用多种方法传递。


public class UserFunctionInterface {
// 定义一个方法以函数式接口作参数
public static void test(ShowInterface shouIn){
shouIn.show();
}

public static void main(String[] args) {
// 1.使用匿名内部类的方式
ShowInterface myfun = new ShowInterface() {
@Override
public void show() {
System.out.println("使用匿名内部类的方式实现函数式接口....");
}
};

test(myfun);

// 2.直接传递匿名内部类
test(new ShowInterface() {
@Override
public void show() {
System.out.println("使用直接传递匿名内部类的方式....");
}
});

// 3.使用Lambda表达式的方式使用函数式接口
test(()-> System.out.println("使用Lambda表达式的方式使用函数式接口..."));
}
}

示例二

@FunctionalInterface
interface MyFunction<T, R> {

R apply(T t);
// 默认方法
default <V> MyFunction<T, V> andThen(MyFunction<R, V> after) {
return (T t) -> after.apply(this.apply(t));
}
}

在这个示例中,MyFunction是一个自定义函数式接口,包含一个抽象方法apply,以及一个默认方法 andThen,用于组合函数。

常用函数式接口

函数式接口类型参数类型用途
Consumer<T> 消费型接口T对类型为T的对象应用操作,包含方法: void accept(T t)
Supplier<T>供给型接口返回类型为T的对象,包含方法:T get()
Function<T, R>功能型接口T对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)
Predicate<T>判断型接口T确定类型为T的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t)

更多其他类型的函数式接口如下图所示:

https://static.yximgs.com/udata/pkg/EE-KSTACK/cc9e78bb3754364718dd44db3dd9e743.png

Supplier接口

java.util.function.Supplier<T>接口仅包含一个无参的方法:T get() 用来获取一个泛型参数指定类型的对象数据。

/**
* 常用函数式接口
* java.util.function.Supplier<T>接口仅包含一个无参构造方法get()
* Supplier<T>接口称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据
*/

public class SupplierDemo {
public static String getTheString(Supplier<String> supplier) {
return supplier.get();
}

public static void main(String[] args) {
String testString = getTheString(() -> "假设它的参数是一个 String 类型");
System.out.println(testString);
}
}

Consumer接口

java.util.function.Consumer<T>接口则正好与supplier接口相反,他不是产生一个数据,而是消费一个数据,其数据类型由泛型决定。

抽象方法:accept

Consumer接口中包含抽象方法 void accept(T t),意为消费一个指定泛型的数据。 Consumer接口是一个消费型接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据。

import java.util.function.Consumer;
import java.util.function.Supplier;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

public class ConsumerDemo {
/*
* 定义一个方法:
* 方法的参数传递Consumer接口,Author
* 可以使用Consumer接口消费Author对象
*/
public static void consumerAuthor(Author author, Consumer<Author> con) {
con.accept(author);
}

public static void main(String[] args) {
Supplier<Author> supplier = () -> new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
consumerAuthor(supplier.get(), (Author author) -> System.out.println(author.getName()));
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Author {
private Long id;
private String name;
private Integer age;
private String desc;
private String img;
}
}

默认方法:andThen

如果一个方法的参数和返回值类型都是Consumer类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中的default方法andThen。

要想实现组合,需要两个或多个Lambda表达式即可,而 andThen的语义正是一步接一步操作。例如两个步骤组合的情况:

        Consumer<Author> consumer1 = s -> System.out.print("作家名:" + s.getName());
Consumer<Author> consumer2 = s -> System.out.println("作家介绍:" + s.getDesc());
Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
consumer1.andThen(consumer2).accept(author);

Predicate接口

有时候我们需要对某种类型的数据进行判断,从而达到一个Boolean值结果,这时可以使用 java.util.function.Predicate<T>接口。

抽象方法:test

Predicate接口中包含一个抽象方法:boolean test(T t).用于条件判断的场景:

import java.util.function.Predicate;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

public class PredicateDemo {
/*
* 定义一个方法:
* 参数传递一个Author类型的字符串,传递一个predicate接口,泛型使用Author
* 使用predicate中的方法test对字符串进行判断,并把判断的结果返回
* */
public static Boolean checkAge(Author author, Predicate<Author> pre){
return pre.test(author);
}

public static void main(String[] args) {
Author menduo = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
boolean b1 = checkAge(menduo ,(Author author)-> author.getAge() > 17);
boolean b2 = checkAge(menduo ,author -> author.getAge() > 17);
System.out.println(b2);
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Author {
private Long id;
private String name;
private Integer age;
private String desc;
private String img;
}
}

默认方法:and

and(Predicate<? super T> other),我们在使用 Predicate 接口时候可能需要进行判断条件的拼接。而and方法相当于是使用&&来拼接两个判断条件

例如:打印作家中年龄大于17并且姓名的长度大于1的作家。

        // 打印作家中年龄大于17并且姓名的长度大于1的作家。
List<Author> authors = Lists.newArrayList(menduo);
authors.stream()
.filter(((Predicate<Author>) author -> author.getAge() > 17)
.and(author -> author.getName().length() > 1))
.forEach(System.out::println);
// 上面 filter 可以拆解为
Predicate<Author> filterPredicate = new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() > 17;
}
}.and(author -> author.getName().length() > 1);
authors.stream()
.filter(filterPredicate)
.forEach(System.out::println);


默认方法:negate

Predicate 接口中的方法。negate 方法相当于是在判断添加前面加了个! 表示取反

例如:打印作家中年龄不大于17的作家。

        // 打印作家中年龄不大于17的作家。
List<Author> authors = Lists.newArrayList(menduo);
authors.stream()
.filter(((Predicate<Author>) author -> author.getAge() > 17).negate())
.forEach(author -> System.out.println(author.getAge()));

Function接口

java.util.function.Function<T,R>接口用来根据一个类型得到另一个类型的数据,前者称为前置条件,后者称为后置条件。

Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。

使用的场景例如:将 String类型转换为 Integer类型。

默认方法 compose(Function<? super V, ? extends T> before),先执行compose方法参数before中的apply方法,然后将执行结果传递给调用compose函数中的apply方法在执行。 andThen(Function<? super R, ? extends V> after),先执行调用andThen函数的apply方法,然后在将执行结果传递给andThen方法after参数中的apply方法在执行。它和compose方法整好是相反的执行顺序。

import java.util.function.Function;

public class FunctionDemo {
/*
* 定义一个方法:方法的参数传递一个字符串类型的整数,一个Function接口,泛型使用<Integer,String>
* 使用Function接口中的方法apply,把Integer类型的整数,转换成字符串
*/
private static void convert(Integer i, Function<Integer, String> fun) {
String s = fun.apply(i);
System.out.println(s);
}

private static void convert(Integer i, Function<Integer, String> fun1, Function<String, String> fun2) {
String str = fun1.andThen(fun2).apply(i);
System.out.println(str);
}

private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
String str = fun2.compose(fun1).apply(s);
System.out.println(str);
}


public static void main(String[] args) {
convert(666, i -> String.valueOf(i));
convert(666, i -> String.valueOf(i), s -> "不殊-" + s);
convert("666", s -> Integer.parseInt(s), s -> "不殊-" + s);
}
}
💡本文声明

转载请注明出处,谢谢合作!转载本文请声明原文章链接如下:

原文链接: https://zhoujun134.github.io/docs/java/06-java-function-interface

作者: Z 不殊

Z 不殊 致力于分享有价值的信息和知识。我们尊重并保护知识产权。本文仅代表作者观点,不代表任何立场。 如果本文有所侵权,请联系作者删除或修改!

Loading Comments...