JAVA8 函数式接口理解

**

最近上了项目,发现对java8的新特性理解不到位。遂整理了一下关于java8函数式接口的知识。

了解函数式接口,个人的学习的路线为:
匿名内部类 -> lambda表达式 -> 四大函数式接口

  1. 匿名内部类
    匿名内部类可以避免继承和实现的时候多写一个单独的class, 在实例化的时候直接通过匿名内部类重写接口后者父类的方法。比如:
public abstract class Preson{
	void eat();
}

public class Child implements Person{
	@override
	void eat(){
		System.out.println("吃奶")}
}

public class Test{
	public static void main(String[] args) {
		Person person = new Child();
		person.eat();
	} 
}

这里返回的就是eat。但是通过匿名内部类,可以避免再去定义一个临时的class child:

Interface Person{
	void eat();
}

public class Test{
	Person person = new Person{
		void eat(){
			System.out.println("吃奶");
		}
	}
	person.eat();
}

就避免了重新去生成一个类来实现或者继承。

  1. lambda表达式
    lambda表达式更加简化了这种匿名内部类的操作,直接使用->就完成了把方法当作参数传递给参数。
    lambda表达式有4个重要特性:

    (1). 只有一个参数时,括号可有可无:
    x -> x + x;
    (2). 可以不用打括号,不用写return
    (x, y) -> x + y;
    (3). 打了括号以后,就一定要有return
    (x, y)-> { return x+y; }
    (4). 参数类型是可选的。
    lambda在匿名内部类的基础上更加简化了代码,并且增加了代码的可读性。

  2. 函数式接口
    函数式接口是有且仅有一个抽象方法的接口,是可以自定义的,用@FunctionalInterface。
    函数式接口的意思和目的,个人的理解为:**把函数当作参数进行传递。**这里参考了这篇博客:

https://juejin.cn/post/6844903892166148110

本篇笔记主要是为了加深自己对java8的四大函数式接口的理解而做的笔记。之前在项目上,在application层做排序和筛选的时候,当时刚上手,不太理解,代码体写的十分混乱。当时被要求用stream来做过滤和排序的时候脑子是昏的,下来以后看了函数式接口后,豁然开朗,明白了以前别人提出的很多思路以及自己确实不该犯的重构错误。下面切入正题,记录一下四大函数式接口:

(1).Consumer <T> 消费型函数式接口
消费型接口的特征是:有输入,无输出。因为是消费者,就是拿来用的。
调用该接口内部使用accept(T t)

Consumer<String> consumer1 = str -> System.out.print("车名:" + str.split(",")[0]);
        Consumer<String> consumer2 = str -> System.out.println("-->颜色:" + str.split(",")[1]);

        String[] strings = {"奥迪,黑色", "五菱宏光,白色"};

        for (String string: strings) {
            consumer1.andThen(consumer2).accept(string);
        }

内部有两方法:
accept()
andThen(Consumer<T t> after) 该方法会先调用accept(),再调用after.accept();

看到这可能会想,刚才不是才说函数式接口只能有一个方法吗?
为什么这里有两个方法。因为andThen这个方法使用default修饰的,java8允许在函数式接口内部为方法加上default修饰符,在调用的时候不用去实现default方法。被default修饰的方法可以有默认的方法体。

这类消费型接口接口很适合用来配合做过滤,或者做值的计算。streampeek方法接受的参数就是一个Consumer Interface。举个例子,当的业务需求是计算旗下超市上个月销售量增长率超过百分之10的门店,只需要在mapper层查出上个月和上上个月的订单,假设在返回的对象内部定义一个计算月增长率Ratio的成员属性和内部方法CalculateRatio,这时候在stream中peek(对象::CalculateRatio)可以在业务层计算月的增长率,提高代码的可读性,为之后过滤做准备。

(2). Supplier 供给者函数式接口
无传参,有返回。这是和消费型接口相反的函数式接口。

Supplier<String> supplier = () -> "呵呵哈!";
        System.out.println(supplier.get());

该接口内部仅有一个方法:
get()
目前在项目上还没有遇到供给型接口的应用场景。暂时知道有这么个玩意儿就可以了。

(3). Function(T t) 功能型函数式接口
有传参,有返回。“功能型接口”,听名字就知道这个玩意儿十分的重要。个人而言,这玩意儿的应用是最多的!

public static void main(String[] args) {
        //有传参,有返回
        Function<Integer, Integer> function1 = it -> it * it;
        Function<Integer, Integer> function2 = it -> it + it;
        Function<Integer, Integer> function3 = Function.identity();

        System.out.println(function1.apply(3));
        System.out.println(function1.compose(function2).apply(3));
        System.out.println(function1.andThen(function2).apply(3));
        System.out.println(function3.apply(3));
        System.out.println("----------------------------------------------");

        List<Integer> integerList = createList();
        System.out.println(integerList.stream().map(function1).collect(Collectors.toList()));
    }

    private static List<Integer> createList() {
        List<Integer> integerList = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            integerList.add(i);
        }
        return integerList;
    }   

输出的结果为:
9
36
18
3
[1, 4, 9, 16, 25]

该函数式接口内有4个方法:
apply
andThen( Function <T t> after)
和Consumer一样,先执行apply,再执行after.apply
compose( Function <T t> before)
和andthen相反,先执行before.apply,再执行apply
identity
返回input
该类型的应用很常见。比如:在复用一个查询的时候,通过stream的map(map的传参是一个function)可以根据get方法来获取想要的数据的List。最开始觉得identity()的应用场景不多,后来发现,在collect(toMap(**, **))的时候,可以传入identity,构建一个id和对象一一对应的map。

(4). Predicate(T t) 断言型函数式接口
有传参,返回一个布尔值
因为Predicate的方法比较多,这里先列一下方法,下面再说应用。该接口内部有5个方法:
boolean test(T t)
and(Predicate<T t>) other作用和&&相同
or(Predicate<T t> other)作用和||相同
nagete作用为取反
isEqual(Object obj)判断是否相等,和Obejects.equals(obj1, obj2)作用一样

		Predicate<Integer> predicate1 = it -> it > 0;
        Predicate<Integer> predicate2 = it -> it < 20;

        System.out.println(predicate1.test(21));
        System.out.println(predicate1.and(predicate2).test(21));
        System.out.println(predicate1.or(predicate2).test(21));
        System.out.println(predicate1.negate().test(21));
        System.out.println(predicate2.negate().test(21));
        System.out.println(Predicate.isEqual(21).test(21));

断言型接口最常用的一个业务场景就是stream的filter了,该方法接受的参数为Predicate函数式接口。结合Consumer中提到的超市的例子,通过peek计算出每个月的增长量后,可以用filter来过滤出每个月增长率Ratio超过百分之10的超市门店。(一般filter能先用就先用,这和Stream PipeLine中的中间操作有关,filter可以减少后面的方法执行的次数。)

关于函数式接口的个人总结就到这里了,希望对大家有帮助。如果之前有两天能拿出来充电,补习一下就好了,最近反思了一下项目上的同事给的意见,确实之前犯的一些java8新特性的问题是可以很快就避免的,可惜。在这个过程中也总结出来一条经验:写代码,看代码,要的是那份冷静,所谓欲速则不达,可以很好的体现在程序员初学者身上,要冷静,不能急躁。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值