当前位置:   article > 正文

Java 函数式编程合集_java函数式编程

java函数式编程

前言


    很多编程语言可以把函数当作参数进行传递,例如js中事件触发后的函数调用、C语言中的函数指针,都可以完成函数的传递。但是在Java里一直没有一个传函数的标准,直到jdk8开始,有了函数传递的一套规范。

1. lambda表达式


1.1 支持版本


    JDK8及以上


1.2 概念


    也叫箭头函数,得益于javac的类型推断,编译器能够根据上下文信息推断出参数的类型。本质上是一个可传递的匿名函数,但是区别于匿名内部类,它不会编译出额外的类,例如 Main$1.class


1.3 基本语法


    

  1. 基本形式:parameter -> expression
  2. 完整形式:(Type parameter1, Type parameter2) -> { code block; return result; }
  3. 省略参数类型: (parameter1, parameter2) -> { code block; return result; }
  4. 单参数省略参数括号:parameter -> { code block; return result; }
  5. 只有一句表达式省略方法体括号:(Type parameter1, Type parameter2) -> expression


1.4 省略关键


    小括号内参数的类型可以省略
    如果小括号内有且仅有一个参数,则小括号可以省略
    如果方法体大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及分号


1.5 注意事项

        lambda表达式一般用作接口的实现上,用于简化代码。

  1. new Thread(new Runnable() {
  2. @Override
  3. public void run() {
  4. }
  5. });//通过匿名内部类创建Runnable接口实现类作为Thread的参数
  6. new Thread(() -> {
  7. });//通过Labmda表达式创建Thread的参数
  8. // Thread一定是只有一个抽象方法

        lambda表达式往往是使用在方法内部的方法,因此lambda表达式里面声明的基本类型变量都会分配到栈上,每个都是线程安全的,因为栈上的变量不共享。如果lambda表达式方法内部需要修改外部变量的状态,这个变量要保证是在堆上分配,且要注意线程安全问题。 

2. 接口式函数


2.1 支持版本


    JDK8及以上


2.2 概念


    JDK8新增了@FunctionalInterface,用来标注一个接口是符合“函数式”的。即,自定义接口有且只有一个抽象方法(可以理解为,创建此接口的对象只要实现一个方法即可,可能因为lambda只能有一个函数),就认为这个接口是符合函数式的,可以使用lambda语法。


2.3 基本语法


    定义一个普通接口,再定义一个抽象方法即可。这里强调因为JDK8版本的接口可能不止有抽象方法,还能有默认方法和静态方法,这些特性在JDK7里是不存在的。


2.4 default、static


    JDK8的新特性,接口可以定义方法的实现了。default修饰的方法叫默认方法,这种方法不需要手动实现,也不会报错,如果默认方法不能满足需求,重写就好了。
    JDK8的新特性,接口支持定义static方法,调用方法和普通静态方法调用一样:接口名.静态方法名。但是要注意,接口的实现类是没有这个静态方法的。强行调用会报错:This static method of interface 接口名 can only be accessed as 接口名.静态方法名


2.5 示例代码

  1. @FunctionalInterface
  2. public interface Operator {
  3. int operation(int a, int b);
  4. default void sayHello() {
  5. System.out.println("Hello everyone.");
  6. }
  7. static void sayBye() {
  8. System.out.println("Bye everyone.");
  9. }
  10. }

3. 函数式编程


3.1 支持版本


    JDK8及以上


3.2 概念


    函数像变量一样使用,能当作入参也能作为返回函数


3.3 实现思路


        借助“接口式函数”和lambda表达式,创建一个函数的调用句柄,参数列表处用接口式函数作为入参,方法体里用接口的唯一抽象方法执行业务逻辑。


3.3 示例代码

3.1 接口部分

  1. // 添加FunctionalInterface注解,表示这是一个方法式接口,不加也行,加上可以让编译器检查语法
  2. // 建议添加,以防他人修改接口
  3. @FunctionalInterface
  4. public interface Operator {
  5. // 只有这个方法是抽象的
  6. int operation(int a, int b);
  7. // 默认方法,子类可以不实现直接用,不满足需求可以重写
  8. default void sayHello() {
  9. System.out.println("Hello everyone.");
  10. }
  11. // 静态方法,子类不可用,只能 Operator.sayBye()
  12. static void sayBye() {
  13. System.out.println("Bye everyone.");
  14. }
  15. }

 3.2 接口式函数及调用

  1. public class CalculateDemo {
  2. public static void main(String [] s) {
  3. /**
  4. * define anonymous function
  5. */
  6. // +
  7. Operator add = (int a, int b) -> a+b;
  8. // FunctionTest 未省略方法体的括号及返回 = (int a, int b) -> {return a+b;};
  9. // -
  10. // Operator subtract = (int a, int b) -> a-b;
  11. // *
  12. // Operator multiply = (int a, int b) -> a*b;
  13. // /
  14. // Operator divide = (int a, int b) -> a/b;
  15. // %
  16. // Operator mod = (int a, int b) -> a%b;
  17. // 定义入参
  18. int a = 10;
  19. int b = 2;
  20. // 调用计算函数时,把操作函数传过去,这里就测了一个相加的
  21. // add可以看作是一个函数,但是它还是一个对象
  22. calculate(add, a, b);
  23. }
  24. private static void calculate(Operator oper, int a, int b) {
  25. // 调用default方法,没重写过的
  26. oper.sayHello();
  27. // 实际上是调用的Operator对象的那个抽象方法
  28. int result = oper.operation(a,b);
  29. System.out.println(result);
  30. // oper.sayBye(); 报错:This static method of interface Operator can only be accessed as Operator.sayBye
  31. // 静态方法只能这样调用,子类是调用不到的
  32. Operator.sayBye();
  33. }
  34. }

4.java.util.function包主要类

        4.1 - 4.4 示例代码均来自Java8之Consumer、Supplier、Predicate和Function攻略 - 掘金

4.1 Supplier

4.1.1 概念

        provider,只出不入的一个类型,负责对外提供数据,类似信号发生器、工厂。

4.1.2 示例代码

  1. @Test
  2. public void test_Supplier() {
  3. //① 使用Supplier接口实现方法,只有一个get方法,无参数,返回一个值
  4. Supplier<Integer> supplier = new Supplier<Integer>() {
  5. @Override
  6. public Integer get() {
  7. //返回一个随机值
  8. return new Random().nextInt();
  9. }
  10. };
  11. System.out.println(supplier.get());
  12. System.out.println("********************");
  13. //② 使用lambda表达式,
  14. supplier = () -> new Random().nextInt();
  15. System.out.println(supplier.get());
  16. System.out.println("********************");
  17. //③ 使用方法引用
  18. Supplier<Double> supplier2 = Math::random;
  19. System.out.println(supplier2.get());
  20. }

4.2 Consumer 

4.2.1 概念

        consumer,消费者,只入不出。负责使用数据,并不会返回。

4.2.2 示例代码

  1. @Test
  2. public void test_Consumer() {
  3. //① 使用consumer接口实现方法
  4. Consumer<String> consumer = new Consumer<String>() {
  5. @Override
  6. public void accept(String s) {
  7. System.out.println(s);
  8. }
  9. };
  10. Stream<String> stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
  11. stream.forEach(consumer);
  12. System.out.println("********************");
  13. //② 使用lambda表达式,forEach方法需要的就是一个Consumer接口
  14. stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
  15. Consumer<String> consumer1 = (s) -> System.out.println(s);//lambda表达式返回的就是一个Consumer接口
  16. stream.forEach(consumer1);
  17. //更直接的方式
  18. //stream.forEach((s) -> System.out.println(s));
  19. System.out.println("********************");
  20. //③ 使用方法引用,方法引用也是一个consumer
  21. stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
  22. Consumer consumer2 = System.out::println;
  23. stream.forEach(consumer);
  24. //更直接的方式
  25. //stream.forEach(System.out::println);
  26. }

4.3 Predicate

4.3.1 概念 

        直译是谓词,理解为判断器就行了。有输入有输出,但是输出一定是bool型。

4.3.2 示例代码

  1. @Test
  2. public void test_Predicate() {
  3. //① 使用Predicate接口实现方法,只有一个test方法,传入一个参数,返回一个bool值
  4. Predicate<Integer> predicate = new Predicate<Integer>() {
  5. @Override
  6. public boolean test(Integer integer) {
  7. if(integer > 5){
  8. return true;
  9. }
  10. return false;
  11. }
  12. };
  13. System.out.println(predicate.test(6));
  14. System.out.println("********************");
  15. //② 使用lambda表达式,
  16. predicate = (t) -> t > 5;
  17. System.out.println(predicate.test(1));
  18. System.out.println("********************");
  19. }

4.4 Functional

 4.4.1 概念

        有输入有输出,且类型可以自定义的一个类型。

4.4.2 示例代码

  1. @Test
  2. public void test_Function() {
  3. //① 使用map方法,泛型的第一个参数是转换前的类型,第二个是转化后的类型
  4. Function<String, Integer> function = new Function<String, Integer>() {
  5. @Override
  6. public Integer apply(String s) {
  7. return s.length();//获取每个字符串的长度,并且返回
  8. }
  9. };
  10. Stream<String> stream = Stream.of("aaa", "bbbbb", "ccccccv");
  11. Stream<Integer> stream1 = stream.map(function);
  12. stream1.forEach(System.out::println);
  13. System.out.println("********************");
  14. }

4.5 整体示例代码 

  1. @Test
  2. public void testFunctions() {
  3. // provider.get
  4. Supplier<Integer> s = () -> new Random().nextInt();
  5. // consumer.accept
  6. Consumer<String> c = System.out::println;
  7. // provide
  8. Integer integer = s.get();
  9. // functional
  10. Function<Integer, String> f = i -> {
  11. if (i >= 0) {
  12. return "functional判断为正整数";
  13. } else {
  14. return "functional判断为负数";
  15. }
  16. };
  17. // function
  18. c.accept(f.apply(integer));
  19. // judgment.test
  20. Predicate<Integer> p = (i) -> i>0;
  21. c.accept(integer+"");
  22. // predicate
  23. if (p.test(integer)) {
  24. c.accept("predicate判断为正整数");
  25. } else {
  26. c.accept("predicate判断为负数");
  27. }
  28. }

5. 总结

        Java的这个函数式编程,总感觉还是很拗,说是传递的函数,实际上是传递函数所属的对象,然后调用接口,并不是纯的传递函数,只不过是借助lambda简化了一下写法,或者是终于官方提供了一个“传函数”的规范。

        最大的好处是能通过Stream方便的处理数据,且性能更好。

6 . 示例

6.1 两次循环过滤数据

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. // 字典
  4. List<Integer> l1 = new ArrayList<Integer>();
  5. l1.add(3);
  6. l1.add(4);
  7. // 数据集合
  8. List<T> l2 = new ArrayList<T>();
  9. l2.add(new T(1, "one"));
  10. l2.add(new T(2, "two"));
  11. l2.add(new T(3, "three"));
  12. l2.add(new T(4, "four"));
  13. l2.add(new T(5, "five"));
  14. l2.add(new T(6, "six"));
  15. l2.add(new T(7, "seven"));
  16. l2.add(new T(8, "eight"));
  17. // 判断哪些对象是在字典中
  18. // 先把数据集合转化成流,开始循环
  19. List<T> nl2 = l2.stream().filter(tItem -> {
  20. // 把字典转换成流,并判断是否和数据流中有匹配
  21. return l1.stream().anyMatch(i -> tItem.key == i);
  22. // 把过滤后的数据返回给一个新的数据集合
  23. }).collect(Collectors.toList());
  24. nl2.stream().forEach(i -> {
  25. System.out.println(i.key+"---"+i.value);
  26. });
  27. }
  28. }
  29. class T {
  30. public T(Integer key, String value) {
  31. super();
  32. this.key = key;
  33. this.value = value;
  34. }
  35. public Integer key;
  36. public String value;
  37. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/903773
推荐阅读
相关标签
  

闽ICP备14008679号