函数式接口-Lambda表达式的主战场

函数式接口

有且仅有一个抽象方法的接口被称为函数式接口。

函数式接口可以使用注解@FunctionalInterface进行校验。如同常用的@Override注解一样,该注解不会对接口/方法产生实质性的修改,只是作校验用。

函数式接口仅是一个接口,它的具体逻辑实现根据调用时输入的Lambda表达式来确定。

另外,函数式接口只是一种被命名的书写格式,它的学习类似于我们学习普通接口,学习for循环一样,它与学习功能型框架的约定使用方式不同。也许在示范使用的过程中,你会想到其他的实现方式,但是功能的实现是因人而异的,如何实现的决定权在于使用者。换句话说,“没吃过猪肉,也要见过猪跑”。

由于函数式接口与Lambda紧密而不可分,我们可以将函数式接口的学习作为进一步掌握Lambda表达式的使用的试验场。

Java为我们提供了许多函数式接口,其中的许多我们之前已经使用过。还有一些默认的接口我更愿意称其为Java提供的函数式接口模板,供人学习和使用。下面通过案列来介绍函数式接口的使用及常见的函数式接口

一、函数式接口的使用

1.作为方法参数

在创建线程并使用的过程中,我们会使用这样的方式

new Thread(
    () -> System.out.println(Thread.currentThread().getName() + " run...")
).start();

其中,Lambda表达式代表的匿名内部类就是一个函数式接口Runnable。它同时使用了@FunctionalInterface注解进行修饰

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
2.作为方法返回值

对于Collection的字类排序,我们常使用Collections.sort()方法

public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

其中,第二个参数Comparator排序器就是一个函数式接口。我们也会使用Lambda表达式来书写排序方式

public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    list.add("a");
    list.add("ccc");
    list.add("c");
    list.add("bb");
    list.add("dddd");

    System.out.println(list); // 排序前

    Collections.sort(list);
    System.out.println(list); // 使用默认排序

    Collections.sort(list, getComparator());
    System.out.println(list); // 使用长度排序

}

private static Comparator<String> getComparator(){

    //匿名内部类
    //        return new Comparator<String>() {
    //            @Override
    //            public int compare(String o1, String o2) {
    //                return o1.length()-o2.length();
    //            }
    //        };

    //Lambda
    return ((o1, o2) -> o1.length()-o2.length());

    //方法引用
    //        return (Comparator.comparingInt(String::length));
}

当然,我们也可以简略的写为

Collections.sort(list, return ((o1, o2) -> o1.length()-o2.length()));

二、其他常见的函数式接口

Java总结了人们可能用到的函数式接口,提供了一些示例模板,我们也可以直接使用

1.Supplier接口

Supplier<T>供应商/生产型接口。T代表要供应的结果,仅有一个T get()方法。

简单来说,调用该接口的目的就是获得某个类型的对象。例如调用Supplier<String>接口就是为了获得String类型的数据。

下面用代码做示例

/**
 * Supplier 生产型接口,返回泛型定义的类型
 */
public class SupplierDemo01 {

    public static void main(String[] args) {

        //使用Lambda表达式作为接口的实现,返回一个字符串结果
        System.out.println(getString(() -> "asd"));

        System.out.println(getInteger(() -> 123));
    }

    //Supplier接口作为方法的参数,接口的实现由调用者实现
    private static String getString(Supplier<String> sup){
        return sup.get();
    }

    private static Integer getInteger(Supplier<Integer> sup){
        return sup.get();
    }
    
}
2.Cunsumer接口

Cunsumer<T>消费者/消费型接口。T代表要用于消费的类型。

简单来说就是,获得参数后,执行某个操作, 不返回结果。这里介绍两个方法

void accept(T t):接收并执行对应操作方法

Consumer<T> andThen(Consumer<? super T> after):消费后执行下一个消费操作方法

在这里,你可能会存在疑问,它有不只一个方法呀,怎么会是函数式接口呢?其实,andThen()方法并不是抽象方法,它是有default实现的:

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

注意:JDK1.8中,允许接口存在默认实现的方法体

下面使用代码来介绍该接口

public static void main(String[] args) {

    operator("bilibili", s -> System.out.println(s));
    operator("bilibili", System.out::println); // 方法引用方式
    System.out.println("。。。。。。");
    operatorTwice("bilibili", System.out::println, s->System.out.println(new StringBuilder(s).reverse().toString()));

}

//消费一次数据
private static void operator(String name, Consumer<String> con){
    con.accept(name);
}

//消费多次数据
private static void operatorTwice(String name, Consumer<String> con1, Consumer<String> con2){
    con1.andThen(con2).accept(name);
    //等同于
    //        con1.accept(name);
    //        con2.accept(name);
}

得到输出结果

bilibili
bilibili
。。。。。。
bilibili
ilibilib

3.Predicate接口

Predicate<T>校验型接口。对提供参数进行断言判断。具体的校验逻辑由调用者的Lambda表达式提供

boolean test(T t):测试方法

Predicate<T> negate():非逻辑方法

Predicate<T> and(Predicate other):与逻辑方法

Predicate<T> or(Predicate other):或逻辑方法

上代码

public static void main(String[] args) {

    System.out.println(checkString("Hello World!", s -> s.length()>5));

    System.out.println(negateCheckString("Hello World!", s -> s.length()>5));

    System.out.println(andCheckString("Hello",
                                      s -> s.length()>8,
                                      s -> s.length()<20
                                     ));

}

private static boolean checkString(String str, Predicate<String> pre){
    return pre.test(str);
}

//非逻辑
private static boolean negateCheckString(String str, Predicate<String> pre){
    return pre.negate().test(str);
    //等同于
    //        return !pre.test(str);
}

//与逻辑
private static boolean andCheckString(String str, Predicate<String> pre1, Predicate<String> pre2){

    return pre1.and(pre2).test(str);
    //等同于
    //        return pre1.test(str) && pre2.test(str);
}

输出结果

true
false
false

4.Function接口

Function<T,R>功能型接口。根据T类型的参数返回R类型的结果。它如同我们普通的方法,接收参数,处理数据,返回结果。

R apply(T t):处理数据方法

Function<T,V> andThen(Function after):处理后执行下一个消费操作方法

上代码

public static void main(String[] args) {

    convert("123",s -> Integer.parseInt(s));

    convert(123, i -> String.valueOf(i+100));

    convert("365",
            Integer::parseInt,
            i -> String.valueOf(i+100));
}

//定义一个方法,将字符串转换为int并输出
private static void convert(String s, Function<String, Integer> fun){
    Integer apply = fun.apply(s);
    System.out.println(apply);
}
//定义一个方法,在int后添加一些数据并输出
private static void convert(Integer i, Function<Integer,String> fun){
    System.out.println(fun.apply(i));
}
//定义一个方法,将字符串转换为int,并在int后添加一些数据,并输出
private static void convert(String s, Function<String,Integer> fun1, Function<Integer,String> fun2){
    //        Integer integer = fun1.apply(s);
    //        String str = fun2.apply(integer);
    //        System.out.println(str);
    //等同于
    System.out.println(fun1.andThen(fun2).apply(s));
}

运行结果

123
223
465

三、总结

  1. 有且只有一个抽象方法的接口被称为函数式接口
  2. 函数式接口可以使用@FunctionalInterface注解进行校验
  3. 函数式接口内抽象方法的实现常用Lambda表达式书写
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值