【Java基础】Java8新特性—快速学会常见的函数式接口及应用

一.函数式接口是什么?

  • 有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,可以使用Lambda表达式创建一个函数式接口的对象,一定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。

  • Java 8中专门为函数式接口引入了一个新的注解:·@FunctionalInterface· 。一旦使用该注解来定义接口, 编译器将会·强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错但是这个注解不是必须的,只要符合函数式接口的定义,那么这个接口就是函数式接口。

  • 函数式接口中只允许有一个抽象方法,但是可以有多个static方法和default方法

二.自定义函数式接口

@FunctionalInterface
public interface MyFunction {
    
    
    void print(String s);
}
public class MyFunctionTest {
    
    
    public static void main(String[] args) {
    
    
        String text = "试试自定义函数好使不";
        printString(text, System.out::print);
    }
    private static void printString(String text, MyFunction myFunction) {
    
    
        myFunction.print(text);
    }
}

三.常见函数式接口

1.Supplier<T>: 供给型接口

  • 抽象方法:T get(),无参数,有返回值。使用时需指定一个泛型来定义参数类型

    @FunctionalInterface
    public interface Supplier<T> {
          
          
        /**
         * Gets a result.
         * @return a result
         */
        T get();
    }
    
  • 练习:求数组最大值

    public class SupplierTest {
          
          
        public static void main(String[] args) {
          
          
            fun1(()->{
          
          
                //执行处理数据逻辑
                int arr[] = {
          
          22,33,55,66,44,99,10};
                // 计算出数组中的最大值
                Arrays.sort(arr);
                return arr[arr.length-1];
            });//
        }
    
    	//获取返回数据进行处理
        private static void fun1(Supplier<Integer> supplier){
          
          
            // get() 是一个无参的有返回值的 抽象方法
            Integer max = supplier.get();
            System.out.println("max = " + max);
        }
    }
    

2.Consumer<T>:消费型接口

  • 抽象方法: void accept(T t),接收一个参数进行消费,但无需返回结果。使用时需指定一个泛型来定义参数类型

    @FunctionalInterface
    public interface Consumer<T> {
          
          
        /**
         * Performs this operation on the given argument.
         * @param t the input argument
         */
        void accept(T t);
    }
    
  • 使用:将输入的数据统一转换为小写输出

    public class ConsumerTest {
          
          
        public static void main(String[] args) {
          
          
            test(msg -> {
          
          
                //编写消费逻辑
                System.out.println(msg + "-> 转换为小写:" + msg.toLowerCase());
            });
            //Hello World-> 转换为小写:hello world
        }
        public static void test(Consumer<String> consumer){
          
          
            //输入消费数据
            consumer.accept("Hello World");
        }
    }
    
  • 默认default方法: andThen(Consumer<? super T> after)先消费然后在消费,先执行调用andThen接口的accept方法,然后在执行andThen方法参数after中的accept方法

        default Consumer<T> andThen(Consumer<? super T> after) {
          
          
            Objects.requireNonNull(after);
            return (T t) -> {
          
           accept(t); after.accept(t); };
        }
    
    public class ConsumerAndThenTest {
          
          
        public static void main(String[] args) {
          
          
            test2(msg1 -> {
          
          
                System.out.println(msg1 + "-> 转换为小写:" + msg1.toLowerCase());
            }, msg2 -> {
          
          
                System.out.println(msg2 + "-> 转换为大写:" + msg2.toUpperCase());
            });
            //Hello World-> 转换为大写:HELLO WORLD
    		//Hello World-> 转换为小写:hello world
        }
    
        public static void test2(Consumer<String> c1, Consumer<String> c2) {
          
          
            String str = "Hello World";
            //c1.accept(str); // 转小写
            //c2.accept(str); // 转大写
            //c1.andThen(c2).accept(str);
            c2.andThen(c1).accept(str);
        }
    }
    
  • 练习:格式化打印信息

    public class ConsumerAndThenTest2 {
          
          
        public static void main(String[] args) {
          
          
            String[] array = {
          
          "大雄,男", "静香,女", "胖虎,男"};
            printInfo(
                    s -> System.out.print("姓名:" + s.split(",")[0] + ","), //姓名不换行打印
                    s -> System.out.println("性别:" + s.split(",")[1] + "。"),//性别换行打印
                    array
            );
            // 姓名:大雄,性别:男。
            // 姓名:静香,性别:女。
            // 姓名:胖虎,性别:男。
        }
    
        private static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) {
          
          
            for (String info : array) {
          
          
                one.andThen(two).accept(info);
            }
        }
    }
    
    

3.Function<T,R>: 函数型接口

  • 有参有返回值的接口,Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件

    • 抽象方法: R apply(T t)传入一个参数,返回想要的结果。
    @FunctionalInterface
    public interface Function<T, R> {
          
          
        /**
         * Applies this function to the given argument.
         * @param t the function argument
         * @return the function result
         */
        R apply(T t);
    }
    
  • 使用:传递进入一个字符串返回一个数字

    public class FunctionTest {
          
          
    
        public static void main(String[] args) {
          
          
            test(msg -> {
          
          
                return Integer.parseInt(msg);
            });
            //apply = 666
        }
    
        public static void test(Function<String, Integer> function) {
          
          
            Integer apply = function.apply("666");
            System.out.println("apply = " + apply);
        }
    }
    
  • 默认方法:andThen,也是用来进行组合操作

        default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
          
          
            Objects.requireNonNull(after);
            return (T t) -> after.apply(apply(t));
        }
    
  • 练习:数据处理

    • 将字符串截取数字年龄部分,得到字符串;

    • 将上一步的字符串转换成为int类型的数字;

    • 将上一步的int数字累加100,得到结果int数字

      public class FunctionAndThenTest {
              
              
          public static void main(String[] args) {
              
              
              String str = "赵丽颖,20";
              int age = getAgeNum(str, //传入字符
                      s -> s.split(",")[1], //将字符串截取数字年龄部分,得到字符串
                      s2 -> Integer.parseInt(s2),//将上一步的字符串转换成为int类型的数字;
                      s3 -> s3 += 100//将上一步的int数字累加100,得到结果int数字
              );
              System.out.println(age);      //120
          }
      
          private static int getAgeNum(String str,
                                       Function<String, String> one,
                                       Function<String, Integer> two,
                                       Function<Integer, Integer> three) {
              
              
              return one.andThen(two).andThen(three).apply(str);
          }
      }
      
  • 默认的compose方法的作用顺序和andThen方法刚好相反

    • compose(Function<? super V, ? extends T> before),先执行compose方法参数before中的apply方法,然后将执行结果传递给调用compose函数中的apply方法在执行。
       Function<Integer, Integer> function1 = e -> e * 2;
       Function<Integer, Integer> function2 = e -> e * e;
       Integer apply2 = function1.compose(function2).apply(3);
       System.out.println(apply2);
      
  • 而静态方法identity则是,输入什么参数就返回什么参数

4. Predicate<T> : 断言型接口

  • 抽象方法: boolean test(T t),传入一个参数,返回一个布尔值。

    • 有参且返回值为Boolean的接口
    @FunctionalInterface
    public interface Predicate<T> {
          
          
        /**
         * Evaluates this predicate on the given argument.
         *
         * @param t the input argument
         * @return {@code true} if the input argument matches the predicate,
         * otherwise {@code false}
         */
        boolean test(T t);
    }
    
    public class PredicateTest {
          
          
        public static void main(String[] args) {
          
          
            test(msg -> {
          
          
                return msg.length() > 3;
            }, "HelloWorld");
            //b:true
        }
    
        private static void test(Predicate<String> predicate, String msg) {
          
          
            boolean b = predicate.test(msg);
            System.out.println("b:" + b);
        }
    }
    

在Predicate中的默认方法提供了逻辑关系操作 and or negate isEquals方法

  • and(Predicate<? super T> other),相当于逻辑&&,当两个Predicate函数的返回结果都为true时才返回true。
  • or(Predicate<? super T> other) ,相当于逻辑||,当两个Predicate函数的返回结果有一个为true则返回true,否则返回false。
  • negate(),这个方法的意思就是取反。
public class PredicateDefaultTest {
    
    
    public static void main(String[] args) {
    
    
        test(msg1 -> {
    
    
            return msg1.contains("H");
        }, msg2 -> {
    
    
            return msg2.contains("W");
        });
       //false
		//true
	   //false
    }

    private static void test(Predicate<String> p1, Predicate<String> p2) {
    
    
        /*boolean b1 = predicate.test(msg);
        boolean b2 = predicate.test("Hello");*/
        // b1 包含H b2 包含W
        
        // p1 包含H 同时 p2 包含W
        boolean bb1 = p1.and(p2).test("Hello");
        // p1 包含H 或者 p2 包含W
        boolean bb2 = p1.or(p2).test("Hello");
        // p1 不包含H
        boolean bb3 = p1.negate().test("Hello");
        
        System.out.println(bb1); // FALSE
        System.out.println(bb2); // TRUE
        System.out.println(bb3); // FALSE
    }
}

练习:数据筛选

  1. 必须为女生;
  2. 姓名为4个字
public class PredicateTest2 {
    
    
    public static void main(String[] args) {
    
    
        String[] array = {
    
    "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女"};
        List<String> list = filter(
                array,
                s -> "女".equals(s.split(",")[1]), //Predicate1 性别为女
                s -> s.split(",")[0].length() == 4//Predicate2 姓名长度等于4
        );
        System.out.println(list);//[迪丽热巴,女, 古力娜扎,女]
    }

    private static List<String> filter(String[] array, Predicate<String> one, Predicate<String> two) {
    
    
        List<String> list = new ArrayList<>();
        for (String info : array) {
    
    
            if (one.and(two).test(info)) {
    
    
                list.add(info);
            }
        }
        return list;
    }
}

四.其他函数式接口

1.Bi类型接口: BiConsumer

BiConsumer、BiFunction、BiPrediate 是 Consumer、Function、Predicate 的扩展,可以传入多个参数,没有 BiSupplier 是因为 Supplier 没有入参

以BiConsumer为例,这个接口跟 Consumer接》很像,都是消费的意思

@FunctionalInterface
public interface BiConsumer<T, U> {
    
    
 
    
    void accept(T t, U u);
 
	/**本接口中的accept先执行,传入的BiConsumer 接口类型的参数,后执行accept*/
    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
    
    
        Objects.requireNonNull(after);
 
        return (l, r) -> {
    
    
            accept(l, r);
            after.accept(l, r);
        };
    }
}
  • 该接口接收2个泛型参数,跟Consumer一样,都有一个 accept方法,只不过,这里的,接收两个泛型参数,对这两个参数做下消费处理;

使用这个函数式接口的终端操作有map的遍历

 Map<String, String> map = new HashMap<>();
        map.put("a", "a");
        map.put("b", "b");
        map.put("c", "c");
        map.put("d", "d");
        map.forEach((k, v) -> {
    
    
            System.out.println(k);
            System.out.println(v);
        });
  • Map接口的终端操作,forEach的参数就是BiConsumer函数接口,对HashMap 的数据进行消费;BiConsumer函数接口还有一个默认函数,andThen,接收一个BiConsumer接口,先执行本接口的,再执行传入的参数

2.UnaryOperator接口

  • 只接收一个泛型参数T,继承Function接口,也就是说,传入泛型T类型的参数,调用apply后,返回也T类型的参数;
    这个接口定义了一个静态方法,返回泛型对象的本身;
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
    
    
    /**
     * Returns a unary operator that always returns its input argument.
     * @param <T> the type of the input and output of the operator
     * @return a unary operator that always returns its input argument
     */
    static <T> UnaryOperator<T> identity() {
    
    
        return t -> t;
    }
}
UnaryOperator<Integer> dda = x -> x + 1;
System.out.println(dda.apply(10));// 11
UnaryOperator<String> ddb = x -> x + 1;
System.out.println(ddb.apply("aa"));// aa1

五.操作基本数据类型的接口

IntConsumer、IntFunction、IntPredicate、IntSupplier、LongConsumer、LongFunction、LongPredicate、LongSupplier、DoubleConsumer、DoubleFunction、DoublePredicate、DoubleSupplier。 常用的函数式接口就那四大接口Consumer、Function、Prediate、Supplier,其他的函数式接口可以去java.util.function这个包下详细的看。

猜你喜欢

转载自blog.csdn.net/qq877728715/article/details/106781098