精华内容
下载资源
问答
  • 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。接下来通过本文给大家介绍Java 8 Function函数式接口函数式接口实例代码,需要的朋友可以参考下
  • 本文给大家分析了Java8默认方法和函数式接口实例其它创建方式,需要的朋友跟着学习下吧。
  • 主要介绍了Kotlin中的sam(函数式接口)详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 主要介绍了Java 8新特性 内建函数式接口详解的相关资料,这里主要介绍4个基本函数式接口,需要的朋友可以参考下
  • 函数式接口

    2020-12-22 06:02:52
    函数式接口 1. 函数式接口 1.1 概述 | 如果说一个接口内有且只有一个方法,而且该方法是一个缺省属性为public abstract方法,该接口可以称之为是一个函数式接口。  自定义函数式接口,还有系统中提供的函数式接口...
  • 下面小编就为大家带来一篇基于Java8 函数式接口理解及测试。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了Java8简单了解Lambda表达式与函数式接口,具有一定参考价值,需要的朋友可以了解下。
  • 主要介绍了浅谈Java 8 新增函数式接口到底是什么,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 函数式接口 一、概念 函数式接口在Java中是指:有且仅有一个抽象方法的接口。 函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。...

    函数式接口

    一、概念
    函数式接口在Java中是指:有且仅有一个抽象方法的接口。
    函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

    二、格式
    只要确保接口中有且仅有一个抽象方法即可:
    说明:函数式接口要求只有一个抽象方法。但是还可以有默认方法、静态方法,只要只有一个抽象方法就可以。

    修饰符 interface 接口名称 {
        public abstract 返回值类型 方法名称(可选参数信息);
    }
    

    由于接口当中抽象方法的public abstract是可以省略的,所以定义一个函数式接口很简单:

    public interface MyFunctionalInterface {
        //抽象方法只有一个
    	void method();
        //可以含有默认方法
        public default void method_1() {
        }
    }
    

    三、@FunctionalInterface注解
    与@Override注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface。该注解可用于一个接口的定义上:

    @FunctionalInterface
    public interface MyFunctionalInterface {
    	void method();
    }
    

    一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。

    问题:为什么要使用该注解来验证一个接口是否是函数式接口呢?
    :因为如果一个接口是函数式接口,可以使用Lambda来简化代码的开发

    四、自定义函数式接口(无参无返回值)
    对于刚刚定义好的MyFunctionalInterface函数式接口,典型使用场景就是作为方法的参数:

    public class Demo04 {
        public static void main(String[] args) {
            show(() -> System.out.println("lambda执行了。。。。"));
        }
        public static void show(MyFunctionalInterface mi) {
            mi.method();// 调用自定义的函数式接口方法
        }
    }
    

    五、练习:自定义函数式接口(有参有返回)
    请定义一个函数式接口Sumable,内含抽象sum方法,可以将两个int数字相加返回int结果。使用该接口作为方法的参数,并进而通过Lambda来使用它。

    @FunctionalInterface
    public interface Sumable {
        int sum(int a, int b);
    }
    
    public class Demo05 {
        public static void main(String[] args) {
            getSum(150,250,(a,b) -> a + b);
        }
        private static void getSum(int a,int b,Sumable sumable){
            int sum = sumable.sum(a, b);
            System.out.println(sum);
        }
    }
    

    方法引用

    一、方法引用代码示例

    // 函数式接口
    @FunctionalInterface
    public interface Printable {
      	//打印字符串str
      	void print(String str);
    }
    
    public class Demo01 {
        public static void main(String[] args) {
            // printString(str -> System.out.println(str));
            // 使用方法引用输出
            printString(System.out::println);
        }
        public static void printString(Printable pt) {
            //传递参数
            pt.print("Hello World");
    
        }
    }
    

    二、方法引用符
    双冒号::为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。

    如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式——它们都将被自动推导。而如果使用方法引用,也是同样可以根据上下文进行推导。

    函数式接口是Lambda的基础,而方法引用是Lambda的孪生兄弟。就是只要可以使用Lambda的地方都可以使用方法引用,没有使用Lambda表达式就不能使用方法引用。

    下面这段代码将会调用println方法的不同重载形式,将函数式接口改为int类型的参数:

    @FunctionalInterface
    public interface PrintableInteger {
      	void print(int str);
    }
    

    由于上下文变了之后可以自动推导出唯一对应的匹配重载,所以方法引用没有任何变化:

    public class Demo01 {
        public static void main(String[] args) {
            // printString(str -> System.out.println(str));
            printString(System.out::println);
        }
        public static void printString(Printable pt) {
            //传递参数
            pt.print(1024);
    
        }
    }
    

    这次方法引用将会自动匹配到println(int)的重载形式。

    三、通过对象名引用成员方法
    一个类中存在一个成员方法:

    public class MethodRefObj {
        public void printUpperCase(String s){
            System.out.println(s.toUpperCase());
        }
    }
    

    函数式接口:

    @FunctionalInterface
    public interface Printable {
      	//打印字符串str
      	void print(String str);
    }
    

    通过对象名引用成员方法

    public class Demo01 {
        public static void main(String[] args) {
            MethodRefObj obj = new MethodRefObj();
            // Lambda省略和推到形式
            // show("helloworld",str -> obj.printUpperCase(str));
            // 方法引用
            show("helloworld",obj::printUpperCase);
        }
        //自定义方法
        public static void show(String s, Printable pt) {
            //调用Printable接口中的方法将s变为大写
            pt.print(s);
        }
    }
    

    说明:
    Lambda表达式格式:(参数) -> {对象.方法名(参数)}
    方法引用格式: 对象::方法名
    下面两种写法是等效的:

    • Lambda表达式:s2 -> methodRefObject.printUpperCase(s2)
    • 方法引用:methodRefObject::printUpperCase
      (使用方法引用前提:使用对象引用成员方法,要求必须有该对象。)

    四、通过类名称引用静态方法
    函数式接口:

    @FunctionalInterface
    public interface Calcable {
      	int calc(int num);
    }
    
    public class Demo02 {
        public static void main(String[] args) {
            // 使用Lambda表达式
            // method(-5,num -> Math.abs(num));
            // 使用方法引用
            method(-5,Math::abs);
        }
        private static void method(int num,Calcable calcable){
            System.out.println(calcable.calc(num));
        }
    }
    

    五、通过super引用成员方法
    如果存在继承关系,当Lambda中需要出现super调用时,也可以使用方法引用进行替代。首先是函数式接口:

    @FunctionalInterface
    public interface Greetable {
      	void greet();
    }
    

    父类:

    public class Fu {
        public void say() {
          	System.out.println("Fu 。。。Hello!");
        }
    }
    

    子类:

    public class Zi extends Fu{
        @Override
        public void say() {
            // lambda写法
            // method(() -> super.say());
            // 方法引用写法
            method(super::say);
        }
        private void method(Greetable lambda) {
            lambda.greet();
            System.out.println("zi.......");
        }
    }
    

    测试:

    public class Test01 {
        public static void main(String[] args) {
            Zi zi = new Zi();
            zi.say();
        }
    }
    

    六、通过this引用成员方法
    this代表当前对象,如果需要引用的方法就是当前类中的成员方法,那么可以使用“this::成员方法”的格式来使用方法引用。首先是简单的函数式接口:

    @FunctionalInterface
    public interface Richable {
       	void buy();
    }
    

    定义一个类

    public class Husband {
        // 定义一个成员方法,供lambda引用
        public void buyHouse(){
            System.out.println("买房子");
        }
        // 函数式接口当参数传递
        private void marry(Richable richable){
            richable.buy();
        }
        // 方法引用
        public void beHappy(){
            // marry(() -> this.buyHouse());
            marry(this::buyHouse);
        }
    }
    

    测试

    public class Test02 {
        public static void main(String[] args) {
            Husband husband = new Husband();
            husband.beHappy();
        }
    }
    

    七、类的构造方法引用
    由于构造方法的名称与类名完全一样,并不固定。所以构造方法引用使用类名称::new的格式表示。
    首先创建一个Person类:
    1)定义一个私有成员变量name;
    2)定义满参构造方法和get set 方法;

    创建函数式接口:PersonBuilder

    @FunctionalInterface
    public interface PersonBuilder {
        // 定义一个抽象方法用来创建Person对象
        Person buildPerson(String name);
    }
    

    测试

    public class Test03 {
        public static void main(String[] args) {
            // lambda省略版
            // printName("王思聪",name -> new Person(name));
            // 方法引用
            printName("迪丽热巴",Person::new);
        }
        public static void printName(String name, PersonBuilder builder) {
            Person p = builder.buildPerson(name);
            System.out.println("姓名:" + p.getName());
        }
    }
    

    八、数组的构造器引用
    数组也是Object的子类对象,所以同样具有构造器,只是语法稍有不同。
    格式:数据类型[]::new
    需求:创建一个长度是10的int类型数组。
    首先创建函数式接口:

    @FunctionalInterface
    public interface ArrayBuilder {
      	int[] buildArray(int length);// length表示数组的长度
    }
    

    测试

    public class Test04 {
        public static void main(String[] args) {
            // lambda简化版
            // int[] arr = initArray(10, length -> new int[length]);
            // 方法引用
            int[] arr = initArray(10,int[]::new);
            System.out.println(arr.length);
        }
        public static int[] initArray(int length,ArrayBuilder builder) {
            int[] arr = builder.buildArray(length);
            return arr;
        }
    }
    

    九、方法引用的总结

    1. 方法引用是lambda另外一种格式,只是这种格式有一些特点,在特定的情况下才可以使用。
    2. 在lambda表达式中,{}里面的内容如果是使用到了其他对象或者是其他类中的功能,这个时候可以采用方法引用简化格式
    3. 如果lambda表达式{}中,功能中使用到的方法是某个类的成员方法,这个时候可以改写为 该类对象::方法名
    4. 如果lambda表达式{}中,功能中使用到的方法是某个类的静态方法,这个时候可以改写为 类名::静态方法名
    5. 如果lambda表达式{}中,功能中使用到的方法是其父类的成员方法,(…)-> super.方法名(…)简写为 super::方法名
    6. 如果lambda表达式{}中,功能中使用到的方法是其类的成员方法,(…)-> this.方法名(…)简写为 this::方法名

    函数式编程

    在兼顾面向对象特性的基础上,Java语言通过Lambda表达式与方法引用等,为开发者打开了函数式编程的大门。

    一、Lambda的延迟执行
    有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。
    性能浪费示例:

    public class Demo01 {
        public static void main(String[] args) {
            String msgA = "Hello ";
            String msgB = "World ";
            String msgC = "Java";
    
            method(1, msgA + msgB + msgC);
        }
        private static void method(int level, String msg) {
            if (level == 1) {
              	System.out.println(msg);
            }
        }
    }
    

    说明:无论级别是否满足要求,作为method方法的第二个参数,三个字符串一定会首先被拼接并传入method方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。

    Lambda的写法:首先定义一个函数式接口

    @FunctionalInterface
    public interface MessageBuilder {  
       	String buildMessage();
    }
    

    测试

    public class Test01 {
        public static void main(String[] args) {
            String strA = "Hello ";
            String strB = "World ";
            String strC = "Java";
            method(1,() -> strA+strB+strC);
        }
        private static void method(int level, MessageBuilder builder) {
            //判断
            if (level == 1) {
                //只有level等于1的时候才会执行这句话,才会执行lambda,然后才能进行拼接
                System.out.println(builder.buildMessage());
            }
        }
    }
    

    说明:由于上述使用了Lambda表达式,Lambda表达式是在执行代码:builder.buildMessage() 时才会执行msgA + msgB + msgC。

    二、使用Lambda作为参数
    Java中的Lambda表达式可以被当作是匿名内部类的另一种体现。如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数。

    代码示例:函数式接口作为参数
    首先定义一个函数式接口

    @FunctionalInterface
    public interface MySupplier {  
      	Object get();
    }
    

    测试

    public class Demo {
         public static void main(String[] args) {
            //使用lambda
           // printObject(()->{return "Hello";});
            //lambda省略格式
            printObject(()->"Hello");
        }
        private static void printObject(MySupplier supplier) {
          	System.out.println(supplier.get());
        }
    }
    

    三、使用Lambda作为返回值
    如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。
    当需要通过一个方法来获取一个java.util.Comparator接口类型的对象作为排序器时:
    需求:定义一个字符串数组按照字符串的长度降序排序。

    public class Demo02 {
        public static void main(String[] args) {
            //定义一个字符串数组
            String[] arr={"abc","ab","abcds","adef"};
            Arrays.sort(arr,getComparator());
            System.out.println(Arrays.toString(arr));
        }
        //定义一个方法来提供自定义比较器Comparator的对象
        public static Comparator<String> getComparator() {
            //使用Lambda表达式返回Comparator的对象
            return (o1,o2)-> o2.length() - o1.length(); // 因为是降序,所以是o2在前面
        }
    }
    

    Lambda的总结:可以理解为1.8之后Lambda的诞生的思想是write less do more(写的少,做的多)。间接可以将Lambda理解为是对匿名内部类的简化,Lambda可以更节省空间和代码量并完成相同的功能。

    常用函数式接口

    我们以前都是自己定义函数式接口,其实JDK本身提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在java.util.function包中被提供。比如:java.util.function.Supplier<T>

    一、Supplier接口
    java.util.function.Supplier接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。
    说明:
    1、这里的泛型T表示当我们使用该接口时,传递什么数据类型就是什么数据类型。然后我们通过这个接口中的get方法就可以获取到指定泛型的类型的对象。
    2、由于Supplier属于函数式接口,我们可以使用Lambda来实现。在Lambda中可以完成Supplier接口中的get()方法体中的代码。主要完成生产某个类的对象数据的功能。即我们需要使用Lambda返回指定泛型类的对象。
    3、其实Supplier接口就是用来生产某个类的对象的。

    需求:指定泛型是String类型,获取String类的对象。

    public class Demo03 {
        public static void main(String[] args) {
            String s = getString(() -> "helloworld");
            System.out.println("s: " + s);
        }
        public static String getString(Supplier<String> lambda){ //Supplier<String> 已经确定泛型T是String类型
            return lambda.get();
        }
    }
    

    说明:上述只是生产String类的对象,那么我们要生产任何类的对象,我们可以修改代码如下所示:

    public class Demo03 {
        public static void main(String[] args) {
            String s = getString(() -> "helloworld");
            System.out.println("s: " + s);
            Integer i = getString(() -> 123);
            System.out.println("i: " + i);
        }
        public static <T> T getString(Supplier<T> lambda){
            return lambda.get();
        }
    }
    

    为什么不直接定义:Integer i =123或者int i = 123;
    原因:
    使用Integer i =123;或者int i=123;这样定义,就把获取对象数据的方式写死了。很不灵活。而使用Supplier接口的方式,我们可以在getObject方法中,除了执行return lambda.get();我们还可以书写其他的代码,完成其他的功能。比如再生产某个对象时,有可能我们还会结合其他的类和对象完成更多的功能。只是我们这里书写的比较简单,简化了业务代码。

    二、Consumer接口
    java.util.function.Consumer<T>接口和Supplier接口正好相反,它不是用来生产一个数据,而是消费一个数据,其数据类型由泛型参数决定。

    抽象方法:accept
    Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据类型的对象。
    由于accept方法有参数,没有返回值,所以该方法只是用来消费接收到的对象数据。

    在使用该接口的时候会确定接口上的泛型,由于这个接口是函数式接口,可以使用Lambda方式来实现。在使用Lambda实现方法体的时候,传递什么数据就对什么数据进行操作,想怎么操作就怎么操作。
    代码示例:需求:使用Consumer接口消费String类型的对象数据。

    public class ConsumerDemo {
        public static void main(String[] args) {
            String s = "Hello World";
            // 可以消费名称
            consumerString(s, System.out::println);
            // 可以消费长度
            consumerString(s,len -> System.out.println(len.length()));
        }
        //定义一个方法使用接口Consumer来消费字符串对象
        public static void consumerString(String s, Consumer<String> lambda) {
            // 使用lambda调用accept方法来消费字符串s
            lambda.accept(s);
        }
    }
    

    通过查看Consumer接口的源代码我们发现,该接口中还有一个默认方法andThen(表示然后的意思)方法。

    默认方法:andThen
    default Consumer<T> andThen(Consumer<? super T> after) {}
    如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中的default方法andThen。下面是JDK的源代码:

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

    说明:
    1、java.util.Objects的requireNonNull静态方法将会在参数after为null时主动抛出NullPointerException异常。这省去了重复编写if语句和抛出空指针异常的麻烦。

    2、return (T t) -> { accept(t); after.accept(t); };表示先执行第一个对象的accept(t)方法,然后在执行第二个对象after.accept(t);方法

    要想实现组合,需要两个或多个Lambda表达式即可,而andThen的语义正是“一步接一步”操作。谁写在前面先执行谁。

    代码示例:使用andThen()方法依次求出字符串"hello"的长度,然后将所有小写字母转换为大写字母。

    public class AndThenDemo {
        public static void main(String[] args) {
            method("hello",s -> System.out.println(s.length()),
                    s -> System.out.println(s.toUpperCase()));
        }
    
        public static void method(String s, Consumer<String> one, Consumer<String> two){
            one.andThen(two).accept(s);
        }
    }
    

    练习:格式化打印信息
    下面的字符串数组当中存有多条信息,请按照格式“姓名:XX。性别:XX。”的格式将信息打印出来。要求将打印姓名的动作作为第一个Consumer接口的Lambda实例,将打印性别的动作作为第二个Consumer接口的Lambda实例,将两个Consumer接口按照顺序“拼接”到一起。

    public class DemoConsumer {
        public static void main(String[] args) {
            String[] arr = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"};
            printInfo(s -> System.out.print("姓名:" + s.split(",")[0]),
                    s -> System.out.println("。性别:" + s.split(",")[1] + "。"),arr);
        }
        private static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) {
            for (String s : array) { // s:迪丽热巴,女  古力娜扎,女  马尔扎哈,男
                one.andThen(two).accept(s); // 姓名:迪丽热巴。性别:女。
            }
        }
    }
    

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

    抽象方法:test
    Predicate接口中包含一个抽象方法:boolean test(T t)
    说明:这个接口就是一个条件判断接口。判断传递的泛型类型的数据是否符合判断条件,而条件的具体代码由Lambda表达式来实现。

    需求:判断指定的字符串 “ajahsgs” 中是否含有a字符。

    public class PredicateDemo {
        public static void main(String[] args) {
            String name="ajahsgs";
            boolean boo = checkString(name, s -> s.contains("a"));
            System.out.println("boo = " + boo);
        }
        public static boolean checkString(String s, Predicate<String> lambda) {
            return lambda.test(s);
        }
    }
    

    默认方法:and
    既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个Predicate条件使用“与”逻辑连接起来实现“并且”的效果时,可以使用default方法and。其JDK源码为:

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);//判断接口对象是否为空
        return (t) -> test(t) && other.test(t);
    }
    

    需求:如果要判断一个字符串既要包含字母“a”,并且长度大于5。

    public class PredicateDemo02 {
        public static void main(String[] args) {
            String name = "ajahsgs";
            boolean boo = checkString(name, s -> s.contains("a"), s -> s.length() > 5);
            System.out.println("boo : " + boo);
        }
        public static boolean checkString(String s, Predicate<String> one, Predicate<String> two) {
            return one.and(two).test(s);
        }
    }
    

    默认方法:or
    与and类似,默认方法or实现逻辑关系中的“或”。JDK源码为:

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
    

    如果希望实现逻辑“一个字符串包含字母a或者长度大于5”,那么代码只需要将“and”修改为“or”名称即可,其他都不变:

    public class PredicateDemo02 {
        public static void main(String[] args) {
            String name = "aja";
            boolean boo = checkString(name, s -> s.contains("a"), s -> s.length() > 5);
            System.out.println("boo : " + boo);
        }
        public static boolean checkString(String s, Predicate<String> one, Predicate<String> two) {
            return one.or(two).test(s);
        }
    }
    

    默认方法:negate
    “与”、“或”已经了解了,剩下的“非”(取反)也很简单。默认方法negate的JDK源代码为:

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    

    从实现中很容易看出,它是执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在test方法调用之前调用negate方法,就是先调用negate方法,在调用test方法。正如and和or方法一样:
    negate()方法演示如下:

    public class PredicateDemo02 {
        public static void main(String[] args) {
            String name = "bjb";
            boolean boo = checkString(name, s -> s.contains("a"));
            System.out.println("boo : " + boo);
        }
        public static boolean checkString(String s, Predicate<String> one) {
            return one.negate().test(s);
        }
    }
    

    练习:集合信息筛选

    /*
        数组当中有多条“姓名+性别”的信息如下,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,
        需要同时满足两个条件:
        1. 必须为女生;
        2. 姓名为4个字。
     */
    public class PredicateDemo03 {
        public static void main(String[] args) {
            String[] array = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男","赵丽颖,女"};
            List<String> list = getList(array, s -> s.split(",")[1].equals("女"), s -> s.split(",")[0].length() == 4);
            System.out.println(list);
        }
        //自定义方法
        public static List<String> getList(String[] arr, Predicate<String> one, Predicate<String> two)
        {
            //创建一个空的集合
            ArrayList<String> list = new ArrayList<>();
            //遍历数组
            for (String s : arr) {
                //s表示取出的每一个数据
                boolean boo = one.and(two).test(s);//如果boo为true,说明满足两个条件
                //判断,满足两个条件,就将s添加到list集合中
                if (boo) {
                    //说明满足两个条件
                    list.add(s);
                }
            }
            //将list返回给调用者
            return list;
        }
    }
    

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

    抽象方法:apply
    Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。
    说明:
    Function接口其实是一个转换接口,具有转换功能,可以将传入的T类型数据转换为R类型数据。转换的操作由Lambda表达式来处理,就是根据类型T作为参数的数据获取R类型的数据。

    示例:将String类型转换为Integer类型。“123”------>转换为123。

    public class FunctionDemo {
        public static void main(String[] args) {
            String s = "123";
            int i = method(s, Integer::parseInt);
            System.out.println(i);
        }
        //定义方法通过接口Function将字符串转换为整数
        public static int method(String s,Function<String,Integer> lambda) {
            return lambda.apply(s);
        }
    }
    

    默认方法:andThen
    Function接口中也有一个默认的andThen方法,用来进行组合操作。JDK源代码如:

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);//判断是否为空
        return (T t) -> after.apply(apply(t));//先执行apply(t),在执行after.apply()
    }
    

    该方法同样用于“先做什么,再做什么”的场景,和Consumer中的andThen差不多:
    需求:将一个字符串"10"先转换位整数10,然后在将转换后的数字10乘以10,并输出结果。

    public class Demo011 {
        public static void main(String[] args) {
            //调用方法
            method("10", Integer::parseInt, i-> i = i*10);
        }
        //定义方法
        public static void method(String s, Function<String,Integer> one, Function<Integer,Integer> two) {
            //调用方法
            int result = one.andThen(two).apply(s);
            //输出结果
            System.out.println("result = " + result);
        }
    }
    

    请注意,Function的前置条件泛型和后置条件泛型可以相同。

    练习:自定义函数模型拼接
    请使用Function进行函数模型的拼接,按照顺序需要执行的多个函数操作为:
    给定字符串:String str = “赵丽颖,20”;

    1. 将字符串截取数字年龄部分,得到字符串;Function<String, String>
    2. 将上一步的字符串转换成为int类型的数字;Function<String, Integer>
    3. 将上一步的int数字累加100,得到结果int数字。Function<Integer, Integer>
    public class DemoFunction {
        public static void main(String[] args) {
            String str = "赵丽颖,20";
            int age = getAgeNum(str, s -> s.split(",")[1],
                                Integer::parseInt,
                                n -> n += 100);
            System.out.println(age);
        }
        private static int getAgeNum(String str, Function<String, String> one,
                                     Function<String, Integer> two,
                                     Function<Integer, Integer> three) {
            //这里先执行one.apply() ,然后是two.apply() 最后是three.apply()
            return one.andThen(two).andThen(three).apply(str);
        }
    }
    

    五、总结:延迟方法与终结方法
    在上述学习到的多个常用函数式接口当中,方法可以分成两种:

    • 延迟方法:只是在拼接Lambda函数模型的方法,并不立即执行得到结果。
    • 终结方法:根据拼好的Lambda函数模型,立即执行得到结果值的方法。

    通常情况下,这些常用的函数式接口中唯一的抽象方法为终结方法,而默认方法为延迟方法。但这并不是绝对的。下面的表格中进行了方法分类的整理:
    在这里插入图片描述

    展开全文
  • Day 28 函数式接口

    2021-01-20 03:14:36
    1. 函数式接口 1.1 概述 如果说一个接口只有一个方法,而且干方法是一个缺省属性为abstract方法,该接口称之为函数式接口。 可以直接使用JDK1.8新特征,Lambda表达式来使用 Lambda表达式对比匿名内部类 1. 简化...
  • 函数式接口定义

    千次阅读 2020-12-24 12:00:59
    一、函数式接口定义 有且仅有一个抽象方法的接口 二、@FunctionalInterface 放在接口定义的上方,表示该接口是函数式接口 接口有且仅有一个抽象方法 2.1、注意 定义函数式接口的时候,@FunctionalInterface是可...

    一、函数式接口定义

    1. 有且仅有一个抽象方法的接口

    二、@FunctionalInterface

    1. 放在接口定义的上方,表示该接口是函数式接口
    2. 接口有且仅有一个抽象方法

    2.1、注意

    1. 定义函数式接口的时候,@FunctionalInterface是可选的,就算不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。

    三、实战

    3.1、函数式接口作为方法的参数

    在这里插入图片描述

    public class Demo1 {
    	
    	public static void main(String[] args) {
    		System.out.println("-----函数式接口作为方法的参数----");
    		method((String s) -> {
    			System.out.println(s);
    		});
    		
    	}
    	
    	public static void method(Toshow toshow) {
    		toshow.toSee("小明");
    	}
    	
    	interface Toshow {
    		void toSee(String userName);
    	}
    }
    

    3.1、函数式接口作为方法的返回值

    在这里插入图片描述

    public class Demo2 {
    	
    	public static void main(String[] args) {
    		Toshow method = method();
    		method.toSee("小苗");
    
    	}
    	
    	public static Toshow method() {
    //        // 匿名内部类实现
    //        return new Toshow() {
    //            @Override
    //            public void toSee(String userName) {
    //                System.out.println(userName);
    //            }
    //        };
    		
    		return (String s) -> {
    			System.out.println(s);
    		};
    		
    	}
    	
    	interface Toshow {
    		void toSee(String userName);
    	}
    	
    }
    

    四、常用的函数式接口

    1. Supplier接口
    2. Consumer接口
    3. Predicate接口
    4. Function接口

    4.1、Supplier

    1. 包含一个无参的方法

      1. T get​():获得结果
      2. 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
    2. Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。

    4.1.1、案例1

    在这里插入图片描述

    public class Demo3 {
    
    	public static void main(String[] args) {
    
    		String s = getString(() -> {
    			return "小苗";
    		});
    
    		System.out.println(s);
    		System.out.println("---------------");
    
    		Integer i = getInteger(() -> 30);
    		System.out.println(i);
    	}
    
    	//定义一个方法,返回一个整数数据
    	private static Integer getInteger(Supplier<Integer> sup) {
    		return sup.get();
    	}
    
    	//定义一个方法,返回一个字符串数据
    	private static String getString(Supplier<String> sup) {
    		return sup.get();
    	}
    	
    }
    

    4.1.2、案例2

    在这里插入图片描述

    用于返回一个int数组中的最大值
    public class Demo5 {
    	
    	public static void main(String[] args) {
    		// 定义一个int数组
    		int[] arr = { 19, 50, 28, 37, 46 };
    		
    		int maxValue = getMax(() -> {
    			int max = arr[0];
    			for (int i = 1; i < arr.length; i++) {
    				if (arr[i] > max) {
    					max = arr[i];
    				}
    			}
    			return max;
    		});
    		System.out.println(maxValue);
    	}
    	
    	// 返回一个int数组中的最大值
    	private static int getMax(Supplier<Integer> sup) {
    		return sup.get();
    	}
    }
    

    4.2、Consumer

    1. 包含两个方法

      1. void accept​(T t):对给定的参数执行此操作
      2. default Consumer andThen​(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行 after操作。这个要看源码才能理解。
    2. 被称为消费型接口,它消费的数据类型由泛型指定。

    4.2.1、案例

    在这里插入图片描述

    public class Demo3 {
    	
    	public static void main(String[] args) {
    		operatorString("ABC", (String s) -> {
    			System.out.println(s.toLowerCase());
    		});
    		
    		System.out.println("-----------");
    		
    		operatorString("ABC", (String s) -> {
    			System.out.println(s + "转小写:" + s.toLowerCase());
    		}, (String s) -> {
    			System.out.println("倒序:" + new StringBuilder(s).reverse().toString());
    		});
    	}
    	
    	// 定义一个方法,用不同的方式消费同一个字符串数据两次
    	private static void operatorString(String name, Consumer<String> con1, Consumer<String> con2) {
    		// con1.accept(name);
    		// con2.accept(name);
    		con1.andThen(con2).accept(name);
    	}
    	
    	// 定义一个方法,消费一个字符串数据
    	private static void operatorString(String name, Consumer<String> con) {
    		con.accept(name);
    	}
    	
    }
    

    4.2.2、案例2

    1. String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33"};
    2. 字符串数组中有多条信息,请按照格式:"姓名:XX,年龄:XX"的格式将信息打印出来
    要求:
    	把打印姓名的动作作为第一个Consumer接口的Lambda实例
    	把打印年龄的动作作为第二个Consumer接口的Lambda实例
    	将两个Consumer接口按照顺序组合到一起使用
    

    在这里插入图片描述

    public class Demo4 {
    	public static void main(String[] args) {
    		String[] strArray = { "林青霞,30", "张曼玉,35", "王祖贤,33" };
    		
    		printInfo(strArray,
    				(String str) -> {
    			String name = str.split(",")[0];
    			System.out.print("姓名:" + name);
    		},
    				(String str) -> {
    			int age = Integer.parseInt(str.split(",")[1]);
    			System.out.println(",年龄:" + age);
    		});
    	}
    	
    	private static void printInfo(String[] strArray, Consumer<String> con1, Consumer<String> con2) {
    		for (String str : strArray) {
    			con1.andThen(con2).accept(str);
    		}
    	}
    }
    

    4.3、Predicate

    1. Predicate接口通常用于判断参数是否满足指定的条件

    2. 常用方法如下

       1. boolean test​(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值 
       2. default Predicate<T> negate​():返回一个逻辑的否定,对应逻辑非( !)
       3. default Predicate<T> and​(Predicate other):返回一个组合判断,对应短路与(&&)
       4. default Predicate<T> or​(Predicate other):返回一个组合判断,对应短路或(||)
      

    4.3.1、案例1

    在这里插入图片描述

    public class Demo1 {
    	
    	public static void main(String[] args) {
    		boolean b1 = checkString("A", (String s) -> {
    			return s.equals("A");
    		});
    		
    		System.out.println("b1:" + b1);
    		
    		boolean b2 = checkString("B", (String s) -> {
    			return s.equals("A");
    		});
    		System.out.println("b2:" + b2);
    
    	}
    	// 逻辑非操作
    	private static boolean checkString(String s, Predicate<String> pre) {
    		// return pre.test(s);
    		// return !pre.test(s);
    		return pre.negate().test(s);
    	}
    }
    

    4.3.1、案例2

    在这里插入图片描述

    public class Demo2 {
    	
    	public static void main(String[] args) {
    		boolean b1 = checkString2("A", (String s) -> {
    			return s.equals("A");
    		}, (String s) -> {
    			return s.equals("B");
    		});
    		
    		System.out.println("b1:" + b1);
    		boolean b2 = checkString3("A", (String s) -> {
    			return s.equals("A");
    		}, (String s) -> {
    			return s.equals("B");
    		});
    		System.out.println("b2:" + b2);
    	}
    	
    	// 逻辑与运算
    	private static boolean checkString2(String s, Predicate<String> pre1, Predicate<String> pre2) {
    		return pre1.and(pre2).test(s);
    	}
    	// 逻辑或运算
    	private static boolean checkString3(String s, Predicate<String> pre1, Predicate<String> pre2) {
    		return pre1.or(pre2).test(s);
    	}
    }
    

    4.3.1、案例3

    1. String[] strArray = {"林青霞,30", "柳岩,34", "张曼玉,35", "貂蝉,31", "王祖贤,33"};
    2. 字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历ArrayList集合
    3. 要求:同时满足如下要求
        1:姓名长度大于2
        2:年龄大于33
    4. 分析:
        1:有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断
        2:必须同时满足两个条件,所以可以使用and方法连接两个判断条件
    

    在这里插入图片描述


    public class Demo3 {
    	
    	public static void main(String[] args) {
    		String[] strArray = { "林青霞,30", "柳岩,34", "张曼玉,35", "貂蝉,31", "王祖贤,33" };
    		ArrayList<String> array = myFilter(strArray,
    				s -> s.split(",")[0].length() > 2,
    				s -> Integer.parseInt(s.split(",")[1]) > 33);
    
    		for (String str : array) {
    			System.out.println(str);
    		}
    	}
    	// 通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中
    	private static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1, Predicate<String> pre2) {
    		// 定义一个集合
    		ArrayList<String> array = new ArrayList<>();
    		// 遍历数组
    		for (String str : strArray) {
    			if (pre1.and(pre2).test(str)) {
    				array.add(str);
    			}
    		}
    		return array;
    	}
    }
    

    4.4、Function<T,R>

    1. Function<T,R> 接口通常用于对参数进行处理,转换,然后返回一个新的值。
    2. 常用的两个方法
      1. R apply​(T t):
        1. 将此函数应用于给定的参数
      2. default Function andThen​(Function after):
        1. 返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果

    4.4.1、案例1

    在这里插入图片描述

    public class Demo4 {
    	
    	public static void main(String[] args) {
    		
    		System.out.println("--------把一个字符串转换int类型,在控制台输出----------");
    		convert("100", s -> Integer.parseInt(s));
    		
    		System.out.println("--------把一个int类型的数据加上一个整数之后,转为字符串在控制台输出----------");
    		convert(100, i -> String.valueOf(i + 566));
    		
    		System.out.println("--------把一个字符串转换int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出----------");
    		convert("100", s -> Integer.parseInt(s), i -> String.valueOf(i + 566));
    	}
    	
    	// 把一个字符串转换int类型,在控制台输出
    	private static void convert(String s, Function<String, Integer> fun) {
    		int i = fun.apply(s);
    		System.out.println(i);
    	}
    	
    	// 把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
    	private static void convert(int i, Function<Integer, String> fun) {
    		String s = fun.apply(i);
    		System.out.println(s);
    	}
    	
    	// 把一个字符串转换int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
    	private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
    		// Integer i = fun1.apply(s);
    		// String ss = fun2.apply(i);
    		// System.out.println(ss);
    		
    		String ss = fun1.andThen(fun2).apply(s);
    		System.out.println(ss);
    	}
    }
    

    4.4.2、案例2

    1. String s = "林青霞,30";
    2. 请按照我指定的要求进行操作:
        1. 将字符串截取得到数字年龄部分
        2. 将上一步的年龄字符串转换成为int类型的数据
        3. 将上一步的int数据加70,得到一个int结果,在控制台输出
    3. 请通过Function接口来实现函数拼接
    

    在这里插入图片描述


    public class Demo5 {
    	
    	public static void main(String[] args) {
    		String s = "林青霞,30";
    		
    		convert(s,
    				(String ss) -> {
    			return s.split(",")[1];
    		},
    				(String ss) -> {
    			return Integer.parseInt(ss);
    		},
    				(Integer i) -> {
    			return i + 70;
    		});
    		
    	}
    	
    	private static void convert(String s, Function<String, String> fun1, Function<String, Integer> fun2,
    			Function<Integer, Integer> fun3) {
    		int i = fun1.andThen(fun2).andThen(fun3).apply(s);
    		System.out.println(i);
    	}
    }
    

    代码地址
    https://gitee.com/DanShenGuiZu/learnDemo/tree/mysql_mybaties_DB/jdk8-learn

    展开全文
  • 函数式接口的学习

    2020-12-22 06:04:07
    函数式接口 如果说一个接口内有且只有一个方法,而且该方法是一个缺省属 性为public abstract方法,该接口可以称之为是一个函数式接口。 Comparator Runnable @FunctionalInterface 使用,检查函数式接口格式问题。...
  • java 函数式接口

    2020-12-22 06:03:26
    函数式接口 1. 概念 函数式接口就是一个有且只有一个抽象方法的接口(可以有其他非抽象的方法)。函数式接口可以转化Lambda表达式。 2. 使用 2.1 @FunctionalInterface 注解使用 @FunctionalInterface 注解,作用是...
  • 常用函数式接口

    千次阅读 2018-05-13 22:27:34
    常用函数式接口JDK 8 中重要的函数接口接口 参数 返回 中文 示例 Supplier None T 提供者 工厂方法创建对象 ​ Consumer T void 消费者 输出一个值 ​ Predicate T boolean 谓语,顾名思义,中文中的‘是’与‘不是...

    常用函数式接口

    JDK 8 中重要的函数接口

    接口      参数          返回          中文          示例
    Supplier None T 提供者 工厂方法创建对象
    
    Consumer T void 消费者 输出一个值
    
    Predicate T boolean 谓语,顾名思义,中文中的‘是’与‘不是’是中文语法的谓语
    
    Function T R 函数 获得某个对象的名字
    
    BinaryOperator (T, T) T 二元操作符,二元(就是数学里二元一次方程那个二元,代表 2 个的意思),双重的。
                    即有两个操作数 例如求两个数的乘积(*)
        
    UnaryOperator T T 一元操作符,只有一个操作数 逻辑非(!)

    Supplier 接口

    Supplier 接口代表一个结果的提供者。
    
    Supplier 接口是用来生成数据的,数据的类型通过泛型参数给定。
    
    使用 get()方法获得返回值,不要求每次调用时都返回新的或不同的结果。

    接口的源码如下:

    @FunctionalInterface
    public interface Supplier {
     /**
    得到一个结果,返回 T 对象
     */
       T get();
      }

    Supplier 接口中的方法

    T get() 获得指定类型的数据

    将 Supplier 直接使用

     案例需求get()方法的基本使用,在 Supplier 接口中 get 方法返回一个字符串。 案例步骤:1) 使用 Lambda 创建 Supplier 对象,泛型类型为 String,方法体返回一个字符串,return 可以省略。2) 调用 Supplier 的 get()方法得到字符串,并打印输出

    public class DemoSupplier {
    
     public static void main(String[] args) {
    
     //使用 Lambda 创建 Supplier 对象
    
     Supplier supplier = () -> "Hello Java!";
    
     //输出它的值
    
     System.out.println(supplier.get());
    
     }
    }

    将 Supplier 使用生成对象

     案例需求:

    下面的例子演示了如何通过调用一个静态方法,生成一个员工对象返回。

    使用构造方法做为 Supplier 参数的引用。

     案例步骤:1) 在主类内部创建一个私有的静态 Employee 对象,重写 toString()方法,返回一个字符串:"我是员工"。

    2) 在 main 函数中创建一个 Supplier 对象,泛型类型是 Employee。使用 Lambda 传入 Supplier 对象,方法体实例化员工对象,省略 return 方法。

    3) 使用 supplier 对象的 get()方法得到员工对象

    4) 打印输出员工对象

    因为 Employee 对象是私有的,外部类无法直接实例化员工对象。

    调用 Supplier 的 get()方法来生成员工对象,这样做的目的是可以控制员工对象的生成方式,类似于工厂模式。

    //主类
    public class DemoSupplier {
    
     //员工类
     private static class Employee {//注意static
    
     @Override
    
     public String toString() {
    
     return "我是员工";
    
     }
    
     }
    
     public static void main(String[] args) {
    
     //使用 Lambda 传入 Supplier 对象,将生成一个员工对象//此时仅仅是实例化了接口并未执行里面代码
    
     Supplier supplier = ()->new Employee();
    
     //输出员工对象
    
     System.out.println(supplier.get());
    
     }
    }

    将 Supplier 做为方法的参数

     需求说明求数组中的最大值,使用 Supplier 接口作为方法参数类型,通过 Lambda 表达式求出 int 数组中的最大值。 需求分析1) 定义整型数组 int[] arr = {12,68,10,2,99,313,46};2) 创建静态方法 getMax():返回 int 类型,将 Supplier 做为参数,泛型类型为 Integer,方法体调用 get()方法返回值。3) 在 main 函数中调用 getMax()方法,使用 Lambda 传入 Supplier 对象,并且实现查找最大值的功能。4) 在 Lambda 表达式相当于方法体:遍历每个元素,比较大小,找出最大值。

    public class DemoSupplier {
     public static void main(String[] args) {
     int[] arr = {12, 68, 10, 2, 99, 313, 46};
     // 调用 getMax 方法获得最大值,Lambda 相当于方法体
     int num = getMax(() -> {
     int max = arr[0];
     for (int i = 1; i < arr.length; i++) {
     if (max < arr[i]) {
     max = arr[i];
     }
     }
     return max;
     });
     //输出最大值
     System.out.println("最大值是:" + num);
     }
     //使用 Supplier 做为参数
     public static int getMax(Supplier<Integer> supplier) {
     return supplier.get();
     }
    }

    Consumer 接口

    Consumer 接口代表接受单一的输入变量而且没有返回值的一类操作。
    
    它的作用和 Supplier 相反,是消费一个数据的,消费的数据类型需要通过泛型指定。

    它的源代码如下:

    
    @FunctionalInterface
    public interface Consumer<T>{
     //接受 t 对象,无返回值
     void accept(T t);
        
    //默认的组合方法,参数和返回值都是 Consumer 类型,先调用自己的 accept()方法,再调用参数的 accept()方法
     default Consumer<T>  andThen(Consumer<T>  after) {
     Objects.requireNonNull(after);
     return (T t) -> { accept(t); after.accept(t); };
     }
    }
    

    Consumer 接口中的方法

    void accept(T t) 接受对给定的参数进行操作。

    Consumer 接口中的默认方法:

    default Consumer<T>  andThen(Consumer<T>  after)然后
    
    如果一个方法的参数和返回值全都是 Consumer<T>  类型,那么就可以实现效果:
    
    消费一个数据的时候,首先做一个操作,然后再做另一个操作,两个操作依次执行,实现一种组合操作。
    
    而这个方法就是 Consumer 接口中的默认方法 andThen。

    直接使用 Consumer 对象

     实现步骤:1) 使用 Lambda 创建 Consumer 对象,直接打印传入的字符串数据。2) 调用 Consumer 的 accept()方法,在 accept()方法中传入一个字符串数据。

    public class DemoConsumer {
     public static void main(String[] args) {
     //创建 Consumer 对象,打印传入的变量 t
     Consumer consumer = t -> System.out.println(t);
     //调用 Consumer 中的方法
     consumer.accept("Hello Lambda");
     }
    }

    Consumer 做为参数

    List  Set 集合中遍历的 forEach 方法它的参数就是 Consumer,

    请看下面的代码: 案例需求:1) 创建一个数组,使用 Arrays.asList("孙悟空", "猪八戒", "白骨精", "嫦娥") 转成 List 对象。2) 使用 forEach 方法打印每一个元素,forEach 中使用 Lamba 表达式输出传入的字符串

    public class DemoForEach {
     public static void main(String[] args) {
     //将数组转成 List 对象
     List names = Arrays.asList("孙悟空", "猪八戒", "白骨精", "嫦娥");
     //打印每一个字符串,forEach 的参数就是 Consumer
     names.forEach(t -> System.out.println(t));
     }
    }

    分析 forEach()方法的源代码

    default void forEach(Consumer<T> action) {
     Objects.requireNonNull(action);//判断某一对象是否不为null
     for (T t : this) {
     action.accept(t);
     }
    }
    
    这是定义在 java.lang.Iterable 接口中的默认方法,
    
    参数就是 Consumer 对象,方法体内对当前集合使用 for遍历,this 就是集合对象。
    
    每次对一个元素调用 accept()方法。
    
    而我们外部调用的代码中对 accept()方法进行了实现,输出了每个元素。
    
    
    public static  T requireNonNull(T obj) 静态方法,JDK7 中新增的方法,判断传入的对象是否为 NULL,
    
    如果是 NULL 则抛出异常,不为 NULL 则返回对象本身。常用于方法或构造方法中传入对象参数的校验。

    default Consumer<T>  andThen(Consumer<? super T>  after) {
     Objects.requireNonNull(after); //判断 after 是否为 null
        
    //先调用自己的 accept()方法,再调用参数的 accept()方法
     return (T t) -> { accept(t); after.accept(t); };
    }
    
    要想实现组合,需要两个或多个 Lambda 表达式,而 andThen 的语义正是执行“一步接一步”操作。

    andThen 方法演示示例 案例需求:将字符串 Hello 首先打印大写的 HELLO,然后打印小写的 hello 实现步骤:1) 创建 Consumer 对象 c1,使用 Lambda 打印 s 对象的大写2) 创建 Consumer 对象 c2,使用 Lambda 打印 s 对象的小写3) c1 调用 andThen(c2)方法,再调用 accept("字符串"),完成依次的操作。

    public class DemoConsumer {
        public static void main(String[] args) {
            //打印大写
            Consumer<String> c1 = s -> System.out.println(s.toUpperCase());
            //打印小写
            Consumer<String> c2 = s-> System.out.println(s.toLowerCase());
            //调用方法
            c1.andThen(c2).accept("Hello Consumer");
        }
    }

    使用 Consumer 做为参数

     需求说明格式化打印信息,下面的字符串数组当中存有多条信息,

    请按照格式“姓名:XX。性别:XX。”的格式将信息打印出来。

    要求将打印姓名的动作作为第一个 Consumer 接口的 Lambda 实例,

    将打印性别的动作作为第二个Consumer 接口的 Lambda 实例,将两个 Consumer 接口按照顺序“拼接”到一起。以下数组共 5 个元素,每个元素包含 2 项信息用逗号分隔。String[] arr = { "张飞,男", "貂蝉,女", "曹操,男","孙尚香,女","小乔,女" };

     实现步骤1) 创建静态方法 printInfo(),有 3 个参数,

    第 1 个是需要打印的字符串数组,

    第 2 个是 Consumer用于打印姓名 name,

    第 3 个是 Consumer用于打印性别 gender。

    2) 在 printInfo 方法中遍历数组中每个元素,再调用 name.andThen(gender).accept(单个元素)

    3) 每调用一次 andThen()方法,在下面输出一行横线

    4) 在 main 函数中创建上面要遍历的数组

    5) 调用 printInfo 方法,传入 3 个参数,

    第 1 个参数是数组,

    第 2 个参数使用 Lambda 打印姓名,参数 s 表示数组中的每个元素。

    第 3 个参数使用 Lambda 打印性别。

    public class DemoConsumerPrint {
        public static void main(String[] args) {
            String[] arr = { "张飞,男", "貂蝉,女", "曹操,男","孙尚香,女"};
    
            //这里的 s 表示数组中的每个元素
            printInfo(arr, s ->{
                System.out.println("姓名:" + s.split(",")[0]);
            },s ->{
                System.out.println("性别:" + s.split(",")[1]);
            });
        }
    
        public static void printInfo(String[] arr, Consumer<String> name, Consumer<String> gender) {
            for (String s : arr) {
                name.andThen(gender).accept(s);
                System.out.println("------------------");
            }
        }
    }

    Predicate 接口

    Predicate 中文意思为谓语,"我是一个程序员""是""不是"就是谓语。
    它代表只有一个变量的函数,返回 boolean 类型。
    
    有时候我们需要进行某种判断,从而得到一个 boolean 值的结果。
    
    可以使用 java.util.function.Predicate接口。

    Predicate 的源码:

    @FunctionalInterface
    public interface Predicate<T> {
     boolean test(T t); //抽象方法,对 t 进行测试,返回 boolean 类型
    
    /**
    - 组合方法,将当前的谓语与另一个谓语进行短路的与操作,返回一个谓语对象
      */
       default Predicate<T> and(Predicate<? super T>  other) {
       Objects.requireNonNull(other); //判断 other 是否为空
       return (t) -> test(t) && other.test(t);
       }
        
       /**
    - 对当前的谓语进行逻辑非操作,返回一个谓语对象
      */
       default Predicate<T> negate() {
       return (t) -> !test(t);
       }
        
       /**
    - 组合方法,将当前的谓语与另一个谓语进行短路的或操作,返回一个谓语对象
      */
       default Predicate<T> or(Predicate<? super T> other) {
       Objects.requireNonNull(other);
       return (t) -> test(t) || other.test(t);
       }
        
       /**
    - 静态方法,判断 test(object)方法传入的对象是否与参数 targetRef 对象相等
      */
       static  Predicate<T> isEqual(Object targetRef) {
       return (null == targetRef)
       ? Objects::isNull
       : object -> targetRef.equals(object); /
       }
      }
    

    Predicate 接口中的方法

    boolean test(T t)  t 进行指定条件的测试,返回 boolean 类型

    test()方法演示

     案例需求:判断 test("字符串")方法给定的参数长度是否大于 5 案例步骤:1) 创建一个 Predicate 谓语对象,使用 Lambda 实现 boolean test(T t)方法2) 方法体的参数是 s,返回字符串的长度大于 5,省略 return 关键字。3) 两次调用 test()方法看运行结果,第 1 次使用字符串 Hello,第 2 次使用字符串 Predicate

    public class DemoPredicateTest {
    
     public static void main(String[] args) {
    
     //创建一个 Predicate 谓语对象,boolean test(T t)方法接收字符串类型,返回 boolean 类型
    
     Predicate<String> predicate = s -> s.length() > 5;
    
     //两次调用 test 方法看运行结果
    
     System.out.println("Hello 的长度是否大于 5:" + predicate.test("Hello"));
    
     System.out.println("Predicate 的长度是否大于 5:" + predicate.test("Predicate"));
    
     }
    }

    默认方法 and()

    既然是条件判断,就会存在与、或、非三种常见的逻辑关系。
    
    其中将两个 Predicate 条件使用“与”逻辑连接起来实现“并且”的效果时,可以使用 default 方法 and。
    
    这个默认方法接收一个 Predicate 参数,返回一个 Predicate参数。

    其 JDK 源码为:

    /**
    组合方法,将当前的谓语与另一个谓语进行短路的与操作,返回一个谓语对象
    */
    
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    and 方法演示示例:

     案例需求:判断一个字符串是否包含指定的字符串:既包含大写“H”,又要包含大写“W” 案例步骤:1) 创建 2 个需要判断的字符串:s1="Hello world"和 s2="Hello World"2) 使用 Lambda 表达式,创建两个 Predicate 对象3) 判断字符串 s 是否包含 H4) 判断字符串 s 是否包含 W5) 调用 and 方法和 test 方法,分别输出 s1 和 s2 的结果

    public class DemoPredicateAnd {
    
     public static void main(String[] args){
    
     //创建 2 个需要判断的字符串
    
     String s1 = "Hello world";
    
     String s2 = "Hello World";
    
     // 使用 Lambda 表达式,创建两个 Predicate 对象
    
     //判断 s 是否包含 H
    
     Predicate<String> p1 = s -> s.contains("H");
    
     //判断 s 是否包含 W
    
     Predicate<String> p2 = s -> s.contains("W");
    
     //调用 and 方法
    
     System.out.println(s1 + "是否包含 H 和 W:" + p1.and(p2).test(s1));
    
     System.out.println(s2 + "是否包含 H 和 W:" + p1.and(p2).test(s2));
    
     }
    }

    默认方法 or()

     and 的“与”类似,默认方法 or 实现逻辑关系中的“或”操作。
    
    这个默认方法接收一个 Predicate 参数,返回一个 Predicate 参数。

    JDK 源码为:

    /**
    *组合方法,将当前的谓语与另一个谓语进行短路的或操作,返回一个谓语对象
    */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
    

    or 方法演示示例:

     案例需求:判断一个字符串的长度大于 10 或者小于 5 案例步骤:1) 创建三个字符串 s1,s2,s3 内容如下图2) 使用 Lambda 创建 2 个 Predicate 接口对象,第 1 个判断长度是否大于 10,每 2 个判断长度是否小于 53) 调用 or 和 test 方法输出每个字符串的测试结果

    public class DemoPredicateOr {
    
     public static void main(String[] args) {
    
     //创建三个字符串
    
     String s1 = "Hello World"; //大于 10
    
     String s2 = "Java"; //小于 5
    
     String s3 = "I am boy"; //既不大于 10,又不小于 5
    
     //使用 Lambda 创建 2 个 Predicate 接口对象
    
     Predicate<String> p1 = s -> s.length() > 10;
    
     Predicate<String> p2 = s -> s.length() < 5;
    
     //输出每个字符串的测试结果
    
     System.out.println(s1 + "=" + p1.or(p2).test(s1));
    
     System.out.println(s2 + "=" + p1.or(p2).test(s2));
    
     System.out.println(s3 + "=" + p1.or(p2).test(s3));
    
     }
    }

    默认方法 negate()

    “与”、“或”已经了解了,剩下的“非”(取反)也会简单。方法没有参数,返回值为 Predicate。

    默认方法 negate的 JDK 源代码为:

    /**
    *对当前的谓语进行逻辑非操作,返回一个谓语对象
    */
    
    default Predicate<T> negate() {
    
     return (t) -> !test(t);
    }
    
    从实现中很容易看出,它是执行了 test 方法之后,对结果 boolean 值进行“!”取反而已。
    
    要在 test 方法调用之前调用 negate 方法,正如 and  or 方法一样。

     案例需求:判断年龄是否小于 18 岁,将判断的结果取反。 案例步骤1) 创建 2 个整数类型的年龄,一个 25,一个 15 岁。2) 使用 Lambda 创建 1 个 Predicate,判断年龄小于 18 岁。3) 使用 nagate()取反以后再调用 test()方法,输出两个年龄的结果

    public class DemoPredicateNegate {
    
     public static void main(String[] args) {
    
     int age1 = 25; //25 岁
    
     int age2 = 15; //15 岁
    
     Predicate<Integer> predicate = (a) -> a < 18; //判断是否小于 18 岁
    
     System.out.println(age1 + "小于 18 岁,取反:" + predicate.negate().test(age1));
    
     System.out.println(age2 + "小于 18 岁,取反:" + predicate.negate().test(age2));
    
     }
    }

    静态方法 isEqual ()

    Predicate 中唯一的静态方法,方法的参数是两个 Object 类型,返回一个 Predicate 类型。
    
    作用:根据 Objects.equals(Object, Object)方法比较两个参数是否相等,
    一个对象通过 isEqual()传入,另一个对象通过 test()传入。
    
    
    java.util.Objects 类中的方法 说明
    public static boolean equals(Object a,Object b)
    
    作用:用于比较两个对象是否相等
    
    参数:a  b 是要比较的两个对象
    
    返回:如果两个对象相等,则返回 true,否则返回 false

    JDK 源代码为:

    /**
    *静态方法,判断 test(object)方法传入的对象是否与参数 targetRef 对象相等
    */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }

     案例需求:比较两个字符串是否相等 案例步骤:1) 通过静态方法 isEqual("newboy"),直接返回 Predicate 对象2) 调用 Predicate 中的 test()方法传入另两个字符串分别比较

    public class DemoPredicateIsEqual {
    
     public static void main(String[] args) {
    
     //通过静态方法直接返回 Predicate 对象
    
     Predicate predicate = Predicate.isEqual("newboy");
    
     //调用 test()方法传入另两个字符串分别比较
    
     System.out.println("两个字符串是否相等:" + predicate.test("newboy"));
    
     System.out.println("两个字符串是否相等:" + predicate.test("NewBoy"));
    
     }
    
    }

    Predicate 的应用示例

     需求说明集合当中有多条“姓名+性别”的信息如下:"张飞,男", "貂蝉,女", "曹操,男","孙尚香,女","小乔,女"

    请通过 Predicate 接口的 and 组合方法,将符合要求的字符串筛选到集合 ArrayList 中,

    需要同时满足两个条件:1) 必须为女生 2) 姓名为两个字

     开发步骤:1) 创建第 1 个 Predicate 判断条件:使用逗号分隔的第 0 个元素姓名长度是 2

    2) 创建第 2 个 Predicate 判断条件:使用逗号分隔的第 1 个元素性别等于女

    3) 创建一个新的 List 集合,用于存储过滤以后符合条件的字符串

    4) 使用 List 中的 forEach(Lambda)遍历上面的原始 List 集合,

    使用 Predicate 中的 and 和 test 方法判断每个元素

    5) 两个条件都为真才添加到新的 List 集合中

    6) 创建第 1 个 Consumer 接口,输出使用逗号分隔的第 0 个元素姓名

    7) 创建第 2 个 Consumer 接口,输出使用逗号分隔的第 1 个元素性别

    8) 使用 List 中的 forEach(Lambda)遍历,输出过滤后的新的集合

    9) 使用 Consumer 接口中的 andThen 和 accept 方法,输出每一个元素

    public static void main(String[] args) {
    
        //从数组中创建一个 List 集合
    
        List<String> list = Arrays.asList("张飞,男", "貂蝉,女", "曹操,男","孙尚香,女","小乔,女");
    
        //创建第 1 个 Predicate 判断条件:使用逗号分隔的第 0 个元素姓名长度是 2
    
        Predicate<String> pname = s -> s.split(",")[0].length() ==2;
    
        //创建第 2 个 Predicate 判断条件:使用逗号分隔的第 1 个元素性别等于女
    
        Predicate<String> pgender = s-> s.split(",")[1].equals("女");
    
        //创建一个新的 List 集合
    
        List<String> infos = new ArrayList<>();
    
        //使用 Lamba 中的 forEach()遍历上面的 List 集合,使用 Predicate 中的 and 和 test 方法判断每个元素
    
        list.forEach(s -> {
    
            //两个都为真才添加到集合中
    
            if (pname.and(pgender).test(s)) {
    
                infos.add(s);
    
            }
    
        });
    
        //创建第 1 个 Consumer 接口,输出使用逗号分隔的第 0 个元素姓名
    
        Consumer<String> cname = s -> System.out.println("姓名:" + s.split(",")[0]);
    
        //创建第 2 个 Consumer 接口,输出使用逗号分隔的第 1 个元素性别
    
        Consumer<String> cgender = s -> System.out.println("性别:" + s.split(",")[1]);
    
        //使用 Lamba 中的 forEach()遍历,输出过滤后的集合
    
        infos.forEach(s -> {
            //使用 Consumer 接口中的 andThen 和 accept 方法,每输出一个元素隔一条线
            cname.andThen(cgender).accept(s);
            System.out.println("---------------");
    
        });
    }

    Function 接口

    Function接口:
            根据一个参数得到另一个参数值,前面称为计算的参数,后面称为计算的结果。
            有进有出,所以称为“函数 Function”。
            
    类似于数学中的函数,通过一个变量求出另一个变量的值。如:f(x) = 2x+3

    以下是它的 Java 源代码

    import java.util.Objects;
    
    /**
    代表通过一个变量求出另一个变量的结果的函数
    
    @param  输入给函数的变量
    
    @param  函数输出的结果
    */
    @FunctionalInterface
    public interface Function<T, R> {
    
     /**
    对给定的变量 t 进行计算,得到返回的结果 R
    */
     R apply(T t);
      
        
     /**
    默认组合方法,先计算当前函数,再计算传入的函数
    */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
        
     /**
    默认组合方法,先计算传入的函数,再计算当前函数
    */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
    
     /**
    静态方法:总是返回它的输入变量
    */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
    }

    抽象方法:apply()

    是java.util.function.Function 接口中的方法 
    
    R apply(T t); 对给定的变量 t 进行计算,得到返回的结果 R

    apply 方法演示示例: 案例需求将 Integer 类型转换为 String 类型,并且输出转换以后字符串的长度。1) 创建一个 Function 对象,输入类型是整数,输出类型是字符串2) Lambda 表达式将一个整数 i 转成字符串3) 调用 apply(数字)方法得到转换后的字符串,再调用字符串的 length()方法得到长度,打印输出。4) 第 1 次转换 99 这个数字,第 2 次转换 1000 这个数字。

    public class DemoFunction {
    
     public static void main(String[] args) {
    
        //创建一个 Function 对象
    
        Function<Integer,String> converter = i -> Integer.toString(i);
    
        System.out.println("99 转成字符串的长度是:" + converter.apply(99).length());
    
        System.out.println("1000 转成字符串的长度是:" + converter.apply(1000).length());
     }
    }

    默认方法:andThen()

    Function 接口中有一个默认的 andThen 方法,用来进行组合操作。
    
    先计算当前函数,再计算传入的函数。两个函数依次执行。
    
    andThen 方法的参数是 Function 对象,返回一个 Function 对象。

    JDK 源代码如:

    /**
    默认组合方法,先计算当前函数,再计算传入的函数
    */
    
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    andThen 方法演示示例: 案例需求:连续进行两个操作:第 1 个操作是将字符串转换成为 int 数字,第 2 个操作将转换好的数字乘以 10。两个操作按照前后顺序组合到一起。1) 让用户从键盘输入 1 个数字,使用字符串接收。2) 创建第 1 个 Function 函数将字符串转成整数3) 创建第 2 个函数将整数乘以 10 返回4) 调用 andThen 方法和 apply,并且输出结果

    public class DemoFunctionAndThen {
        public static void main(String[] args) {
            //用户输入一个字符串
            System.out.println("请输入数字:");
            Scanner input = new Scanner(System.in);
            String str = input.nextLine();
            //第 1 个函数将字符串转成整数
            Function<String,Integer> f1 = s -> Integer.parseInt(s);
            //第 2 个函数将整数乘以 10 返回
            Function<Integer,Integer>  f2 = i -> i * 10;
            //调用 andThen 方法,并且输出结果
            System.out.println("转成整数并乘以 10 以后的结果是:" + f1.andThen(f2).apply(str));
        }
    }

    默认方法:compose()

    Function 中有一个与 andThen 非常类似的 compose 方法。
    
    中文是"组成"的意思,方法参数是 Function,返回值是 Function,
    
    先运行参数的 apply 方法,再调用自己的 apply 方法。

    其 JDK 源代码为:

    /**
    默认组合方法,先计算传入的函数,再计算当前函数
    */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
    
    结合 andThen 方法的 JDK 源码实现进行对比,会发现 compose 方法的参数 Lamda 将会先执行。
    
    所以二者只是先后顺序的不同而已。

    compose 方法的演示

     案例需求:创建两个函数对象:1 个将字符串转成大写,1 个将字符串转成小写。分别使用 andThen 和 compose 方法组合调用,查看不同的计算结果。 开发步骤:1) 创建第 1 个 Function,输入输出都是 String 类型,将字符串转成大写。2) 创建第 2 个 Function,输入输出都是 String 类型,将字符串转成小写。3) 调用第 1 个函数的 apply 方法,并且输出值4) 调用第 2 个函数的 apply 方法,并且输出值5) 调用 andThen 方法和 apply 方法查看运行结果6) 调用 compose 方法和 apply 方法查看运行结果

    public class DemoFunctionCompose {
        public static void main(String[] args) {
            Function<String,String> f1=s -> s.toUpperCase();
            Function<String,String> f2 = s -> s.toLowerCase();
            System.out.println("转成大写:" + f1.apply("Hello"));
            System.out.println("转成小写:" + f2.apply("Hello"));
            System.out.println("先转成大写,再转成小写:" + f1.andThen(f2).apply("Hello"));
            
            System.out.println("先转成小写,再转成大写:" + f1.compose(f2).apply("Hello"));
        }
    }

    Function 的应用示例 需求说明请使用 Function 进行函数拼接,按照顺序执行多个函数。操作依次为:1) 将字符串"赵丽颖,20"截取数字年龄部分,得到字符串;2) 将上一步的字符串转换成为 int 类型的数字;3) 将上一步的 int 数字累加 100,得到结果 int 数字。 开发步骤:1) 创建第 1 个 Function 对象,将字符串 20 取出,返回一个字符串2) 创建第 2 个 Function 对象,将字符串转成整数,返回整数3) 创建第 3 个 Function 对象,将整数加 100,返回计算结果4) 调用 andThen 方法 2 次,apply 方法应用字符串:"赵丽颖,20",输出结果 代码实现

    public class DemoFunctionApp {
        public static void main(String[] args) {
            //创建第 1 个 Function 对象,将字符串 20 取出,返回一个字符串
            Function<String,String> fun1 = s -> s.split(",")[1];
            //创建第 2 个 Function 对象,将字符串转成整数,返回整数
            Function<String,Integer> fun2 = s -> Integer.parseInt(s);
            //创建第 3 个 Function 对象,将整数加 100,返回计算结果
            Function<Integer,Integer> fun3 = num -> num + 100;
            //调用 andThen 方法 2 次,apply 方法应用字符串,输出结果
            System.out.println("计算结果:" + fun1.andThen(fun2).andThen(fun3).apply("赵丽颖,20"));
        }
    }

    BinaryOperator 接口

    BinaryOperator 表示对两个相同类型的操作数进行操作,产生相同类型的结果。

    接口中的方法

    static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) 
    返回一个BinaryOperator ,它根据指定的Comparator返回两个元素中的较大Comparator   
    
    static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) 
    返回BinaryOperator返回根据指定的两个元件的较小的Comparator   
    
    这个接口中定义了两个静态方法,
    BiFunction 是用于定义两个操作符的函数接口。

    BinaryOperator 接口中的方法

    T apply(T t, T u); 从父接口 BiFunction 中继承下来的抽象方法,
    传入两个参数 t  u 进行函数计算,返回计算的结果。
    
    两个参数和返回值都是同一种类型。
    
    
    default <V> BiFunction<T,U,V> andThen(Function<? super R,? extends V> after)
    返回一个组合函数,首先将该函数应用于其输入,然后将after函数应用于结果。 
    如果任一函数的评估引发异常,则将其转发给组合函数的调用者。 
    
    参数类型 
    V - after函数的输出类型,以及组合函数 
    参数 
    after - 应用此函数后应用的功能 
    结果 
    一个组合函数首先应用此函数,然后应用 after函数 
    

    方法的演示:apply()

     案例需求:使用 BinaryOperator 接口的 apply()方法,计算 2 个整数的和,并且输出结果。 案例步骤:1) 创建类 Demo25BinaryOperator2) 创建 BinaryOperator 接口,使用 Lambda 实现方法,方法有两个参数,返回方法的计算结果。3) 调用 apply()方法传入实际的参数,打印计算结果。 案例代码:

    public class DemoBinaryOperator {
        public static void main(String[] args) {
    
            BinaryOperator<Integer> operator = (m, n) -> m + n;
    
            System.out.println("计算结果是:" + operator.apply(3, 5));
        }
    }

    静态方法

    
    public static  BinaryOperator minBy(Comparator comparator)
    通过后面的 Comparator 比较器判断,返回两个元素中较小的元素
    
    public static  BinaryOperator maxBy(Comparator comparator)
    通过后面的 Comparator 比较器判断,返回两个元素中较大的元素
    
    Comparator 接口中的静态方法说明
    naturalOrder() 按元素的自然排序的大小进行比较,返回一个 Comparator 对象
    reverseOrder() 按元素的倒序大小进行比较,返回一个 Comparator 对象

    BinaryOperator 接口做为方法参数

     案例需求有如下数组{2,1,3,5},对数组中的每个元素进行替换。替换算法如下:1) 第 0 个元素不变2) 第 0 个+第 1 个元素的结果代替第 1 个元素3) 第 1 个新元素+第 2 个元素的结果代替 2 个4) 第 2 个新元素+第 3 个元素的结果代替第 3 个5) 依次类推,直到所有的元素替换完成为止。

     案例步骤

    Arrays 类中的方法 说明
    void parallelPrefix(T[] array, BinaryOperator op)
    并行累积
    
    作用:对数组中每个元素使用指定的二元操作函数进行替换操作
    
    参数 1:要替换的数组
    
    参数 2:指定二元操作函数

    1) 创建 BinaryOperator对象,指定 2 个数的算法是 m+n2) 创建 Integer 类型的数组:{2,1,3,5}3) 输出操作前的数组4) 调用上面的 parallelPrefix()方法,将 BinaryOperator 做为参数传入5) 输出操作后的数组6) 如果使用不同的算法,则每个元素的替换的结果不同。如:换成两个数相乘。 案例代码

    public static void main(String[] args) {
        BinaryOperator<Integer> operator = (m,n) -> m+n;
        Integer [] arr = {2,1,3,5};
        System.out.println("操作前的数组:" + Arrays.toString(arr)) ;
        Arrays.parallelPrefix(arr,operator);
        System.out.println("操作后的数组:" + Arrays.toString(arr)) ;
    }

    静态方法的演示 案例需求比较两个整数,使用 minBy 静态方法找出最小值比较两个字符串,使用 maxBy 静态方法找出最大值 案例步骤1) 创建 BinaryOperator对象,使用 minBy()静态方法,按数字的正常大小进行比较。2) 输出最小值,调用 apply()方法,传入 2 个整数。3) 创建 BinaryOperator对象,使用 maxBy()静态方法,按字符串的大小进行比较。4) 输出最大值,调用 apply()方法,传入 2 个字符串:"ABCD","xyz" 案例代码

     public static void main(String[] args) {
     //naturalOrder()是 Comparator 中的静态方法,即按数字的正常大小进行比较
     BinaryOperator oper1 = BinaryOperator.minBy(Comparator.naturalOrder());
     System.out.println("最小值是:" + oper1.apply(3,5));
         
     //naturalOrder()是 Comparator 中的静态方法,即按字符串的正常大小进行比较
     BinaryOperator oper2 = BinaryOperator.maxBy(Comparator.naturalOrder());
     System.out.println("最大值是:" + oper2.apply("ABCD","xyz"));
    }

    UnaryOperator 接口

    UnaryOperator 表示对单个操作数的操作,该操作数生成与其操作数类型相同的结果。
    
    UnaryOperator 接口继承于 Function 接口,
    
    所以有 T apply(T t)抽象方法,与前面的 Function 接口中的 apply()方法相同。
    
    它的输入类型和返回类型是相同的类型。

    UnaryOperator 接口的源码

    package java.util.function;
    @FunctionalInterface
    public interface UnaryOperator extends Function {
     /**
    始终返回其输入参数的一元运算符
    */
     static  UnaryOperator identity() {
     return t -> t;
     }
    }

    方法的演示

    UnaryOperator 接口中的方法 说明
    
    T apply(T t); 
         Function 接口中继承下来的抽象方法,使用给定的参数应用此一元运算函数,返回另一个值。
        参数和返回值是同一种类型。
    
    static  UnaryOperator identity() 
        始终返回其输入参数的一元运算符也就是后续 apply()输入的是什么,就返回什么。

     案例步骤1) 使用 UnaryOperator.identity()静态方法创建 UnaryOperator对象2) 应用 apply()方法,输入字符串 abc,得到结果也是 abc。 案例代码

     public static void main(String[] args) {
     //创建一个 UnaryOperator对象,
     UnaryOperator operator = UnaryOperator.identity();
         
     //调用 apply()方法,输出参数的值
     System.out.println("输出与输出一样:" + operator.apply("abc"));
    }

    UnaryOperator 使用方法的参数

     案例需求:有一个整数的列表集合,将集合中每个元素乘以 2,再替换这个元素,输出替换前后的列表集合有一个字符串的列表集合,将集合中每个元素用它的大写进行替换。 案例步骤:

    ArrayList 中的方法 说明
    replaceAll(UnaryOperator operator) 使用一元操作函数的结果,替换列表中的每个元素

    1) 使用 Arrays.asList()创建一个整数列表2) 创建 UnaryOperator一元运算函数,指定运算表达式是 x*23) 调用 ArrayList 的 replaceAll()方法,把上面创建的一元运算函数做为参数传入4) 输出替换前后的列表5) 使用 Arrays.asList()创建一个字符串列表6) 这次直接在 replaceAll()方法中传入 Lambda 表达式,s.toUpperCase()7) 输出替换前后的列表 案例代码:

     public static void main(String[] args) {
     List nums = Arrays.asList(3, 10, 8, 2);
     System.out.println("替换前:" + nums);
     UnaryOperator oper = x -> x * 2;
     nums.replaceAll(oper);
     System.out.println("替换后:" + nums);
         
     List names = Arrays.asList("Jack","Rose","Tom","NewBoy");
     System.out.println("替换前:" + names);
     names.replaceAll(s -> s.toUpperCase());
     System.out.println("替换后:" + names);
    }

    常用的函数式接口小结

    Supplier 提供数据者 
        T get();没有传入参数,有结果。
    
    Consumer 消费数据者 
        void accept(T t); 传入数据,没有结果。 
        andThen()
        
    Predicate 谓语 
        boolean test(T t); 对传入的数据逻辑判断 
        and()
        or()
        negate()
        isEqual()
        
    Function 函数 
        R apply(T t); 传入一个变量返回计算结果 
        andThen()
        compose()
        identity()
        
        
    BinaryOperator 二元操作符 
        T apply(T t,T u); 传入两个参数返回一个结果 
        andThen()
        继承于 BiFunction
        
    UnaryOperator
        继承于 Function
        一元操作符 
        T apply(T t); 传入一个参数返回一个结果 
        andThen()
        compose()
        identity()

    展开全文
  • 函数式接口的的使用

    2020-07-28 16:32:07
    函数式接口的使用 Function、Predicate、Supplier、Consumer 介绍 什么是函数式接口 接口中只有一个抽象方法(不包括默认、静态) 如何去定义一个函数式接口 @FunctionalInterface 该注解的作用:约束接口中只能有一...

    函数式接口的使用

    Function、Predicate、Supplier、Consumer

    介绍

    什么是函数式接口

    接口中只有一个抽象方法(不包括默认、静态)

    如何去定义一个函数式接口

    @FunctionalInterface 该注解的作用:约束接口中只能有一个抽象方法

    定义一个接口 提供一个抽象方法

    @FunctionalInterface
    interface Cal{
        int calOperation(int a,int b);
    }
    

    Lambda表达式的格式

    (参数) 调用方法、传递参数

    ​ —> 将小括号中的参数传递到大括号中

    ​ {} 实现代码的方法体

    特点

    通过传入不同的function,实现了【同一个方法中实现不同的操作,在实际开发中可以大大减少很多重复的代码,实现逻辑的复用】

    函数式编程和非函数式编程的区别

    • 函数式编程:先考虑传入的参数 再考虑方法的实现
    • 非函数式编程:先定义好方法,再传入指定的参数
    /**
     * @author zhanghuan
     * @date 2020/6/29 11:35 上午
     */
    public class FuncationalInterfaceTest {
    
        public static void showTime(CurrentTimePrinter printer){
            printer.printCurrentTime();
        }
        public static void getResult(int a,int b,Cal cal){
            int res = cal.calOperation(a,b);
            System.out.println(res);
        }
        public static void convert(int num,NumbertoString numbertoString){
            String s = numbertoString.convert(num);
            System.out.println(s);
        }
        public static void main(String[] args) {
            showTime(()-> System.out.println(System.currentTimeMillis()));
            getResult(10,5,(a,b)->a*b);
            convert(999,Integer::toHexString);
        }
    }
    
    @FunctionalInterface
    interface CurrentTimePrinter{
        void printCurrentTime();
    }
    @FunctionalInterface
    interface Cal{
        int calOperation(int a,int b);
    }
    @FunctionalInterface
    interface NumbertoString{
        String convert(int num);
    }
    

    使用函数式编程来实现延迟加载

    Predicate的使用(构造断言式):

    简介:

    predicate是一个接口,含有一个抽象方法test(),含有4个由default修饰的具体实现方法and()、or()、negate()、isEquals()

    and()对应java的连接符 &&;

    or()对应java的连接符 || ;

    negate()对应java的连接符 ! ;

    isEquals对应java的连接符 == ;

    /**
     * Predicate<T>函数式接口
     *
     * @author zhanghuan
     * @date 2020/6/29 3:17 下午
     */
    public class Test03 {
        /**
         * boolean test(T t);  获取布尔类型结果
         * @param s
         * @param pre
         * @return
         */
        public static boolean checkString(String s, Predicate<String> pre){
            return pre.test(s);
        }
    
        /**
         * and();   代表&&的意思
         *
         *   传递两个Predicate接口,比如:
         *  一个用于判断字符串的长度是否大于5
         *  一个用于判断字符串中是否包含a
         *   两个条件必须同时满足
         * @param s
         * @param pre
         * @return
         */
        public static boolean checkStringAnd(String s, Predicate<String> pre1,Predicate<String> pre2){
            return pre1.and(pre2).test(s);
        }
    
        /**
         * or();   代表||的意思
         * @param s
         * @param pre1
         * @param pre2
         * @return
         */
        public static boolean checkStringOr(String s, Predicate<String> pre1,Predicate<String> pre2){
            return pre1.or(pre2).test(s);
        }
    
        /**
         * negate取反
         * @param s
         * @param pre
         * @return
         */
        public static boolean checkStringNegate(String s, Predicate<String> pre){
            return pre.negate().test(s);
        }
    
        /**
         * 方法的参数传递一个包含人员信息的数组
         * 传递两个Predicate接口,用于对数组中信息进行过滤
         * 把满足条件的信息存到ArrayList集合中并返回
         *
         * @return
         */
        public static ArrayList<String> filter(String arr[],Predicate<String> pre1,Predicate<String>pre2){
            ArrayList<String> list = new ArrayList<>();
            for (String s:arr){
                boolean b = pre1.and(pre2).test(s);
                if (b){
                    list.add(s);
                }
            }
            return list;
        }
    
    
        public static void main(String[] args) {
            String s = "abcdef";
            //长度大于5
            boolean b = checkString(s,str->str.length()>5);
            System.out.println(b);
    
            //长度大于6并且包含"a"
            boolean and = checkStringAnd(s,str->str.length()>6,str->str.contains("a"));
            System.out.println(and);
    
            //长度大于6 或者 包含"a"
            boolean or = checkStringOr(s,str->str.length()>6,str->str.contains("a"));
            System.out.println(or);
    
            //取反
            boolean negate = checkStringNegate(s,str->str.length()>5);
            System.out.println(negate);
    
            //过滤操作
            String arr[] = {"张小三,男","小红,女","李四,男","小小明,男"};
            ArrayList<String> list = filter(arr, str -> "男".equals(str.split(",")[1])
                    , str -> str.split(",")[0].length() > 2);
            list.forEach(System.out::println);
    
    
        }
    }
    

    Supplier函数式接口-用来做生产的 T get();

    /**
     * Supplier<T>函数式接口-用来做生产的
     *
     * T get();
     *
     * @author zhanghuan
     * @date 2020/6/29 2:22 下午
     */
    public class Test01 {
        public static void main(String[] args) {
            String s = getString(()->{
                return "hello world";
            });
            System.out.println(s);
    
            //优化lambda
            String s2 = getString(()->"hello java");
            System.out.println(s2);
    
            int [] arr = {34,45,23,1,345,-54};
            int maxVal = getMax(()->{
                int max = arr[0];
                for (int i:arr) {
                    if (i>max){
                        max = i;
                    }
                }
                return max;
            });
            System.out.println(maxVal);
        }
    
        /**
         * 会返回一个String
         * @param sup
         * @return
         */
        public static String getString(Supplier<String> sup){
            return sup.get();
        }
    
        /**
         * 返回一个最大值
         * @param sup
         * @return
         */
        public static int getMax(Supplier<Integer> sup){
            return sup.get();
        }
    }
    

    Consumer函数式接口-用来做消费的

    /**
     * Consumer<T t>函数式接口-用来做消费的
     *
     *
     * @author zhanghuan
     * @date 2020/6/29 2:47 下午
     */
    public class Test02 {
        public static void method(String name, Consumer<String> consumer){
            consumer.accept(name);
        }
        public static void method02(String name, Consumer<String> con1,Consumer<String> con2){
            //使用andThen方法,先把两个Consumer接口连接在一起,在消费数据
            //con1连接con2,先执行con1消费数据,在执行con2消费数据
            con1.andThen(con2).accept(name);
        }
        public static void printInfo(String[] arr,Consumer<String> con1,Consumer<String> con2){
            for (String message: arr){
                con1.andThen(con2).accept(message);
            }
        }
        public static void main(String[] args) {
            method("neoooo",(name)->{
                //对传递的字符串进行消费
                //消费方式:直接输出字符串
                //System.out.println(name);
    
                //消费方式:把字符串进行反转输出
                String reverse = new StringBuffer(name).reverse().toString();
                System.out.println(reverse);
            });
            //调用method02方法,传递一个字符串,两个lambda表达式
            method02("nizhenniubi",(name)->{
                System.out.println(name.toUpperCase());
            },(name)->{
                System.out.println(name.toLowerCase());
            });
            String arr[] = {"zhangsan,male","lisi,male","wanger,female"};
            printInfo(arr,(message)->{
                String name = message.split(",")[0];
                System.out.println("姓名:"+name);
            },(message)->{
                String sex = message.split(",")[1];
                System.out.println("性别:"+sex+" ");
            });
        }
    }
    

    Function<T t,R r>函数式接口
    R apply(T t); 数据类型转换

    /**
     * @author zhanghuan
     * @date 2020/6/29 4:29 下午
     */
    public class Test04 {
        /**
         * Function<T t,R r>函数式接口
         * R apply(T t);    数据类型转换
         * @param s
         * @param function
         */
        public static void change(String s, Function<String,Integer> function){
            //字符串类型的整数 转换为Integer类型的整数
            int in = function.apply(s);
            System.out.println(in);
        }
    
        /**
         * andThen(Function<T t,R r>);
         * @param s
         * @param fun1
         * @param fun2
         */
        public static void change02(String s,Function<String,Integer> fun1,Function<Integer,String>fun2){
            String ss = fun1.andThen(fun2).apply(s);
            System.out.println(ss);
        }
    
        public static int change03(String s,Function<String,String> fun1,Function<String,Integer>fun2,Function<Integer, Integer> fun3){
            return fun1.andThen(fun2).andThen(fun3).apply(s);
        }
        public static void main(String[] args) {
            String s = "12345";
            String ss = "小小明,20";
            change(s,str->Integer.parseInt(s));
            change02(s,str->Integer.parseInt(s)+10,i->i+"");
            int num = change03(ss, str -> str.split(",")[1], str -> Integer.parseInt(str), i -> i + 100);
            System.out.println("num: "+num);
        }
    }
    

    需要注意的:

    • 现有的大多数标准函数接口都支持基本类型。千万不要用带包装类型的基础函数接口来代替基本函数接口。虽然可行,但是它破坏了“基本类型优于装箱基本类型”。使用装箱基本类型进行批量操作处理,最终会导致致命的性能问题

    Reference

    https://www.cnblogs.com/Auge/p/11528974.html

    https://blog.csdn.net/jmj18756235518/article/details/81490966

    展开全文
  • Java8函数式接口与Lambda表达式

    千次阅读 多人点赞 2019-04-07 17:14:10
    何为函数式接口?...函数式接口与lambda表达式的联系:lambda是实现函数式接口的一个快捷方式,可以作为函数式接口的一个实例。常用Java8内置的函数式接口 Function、Predicate、Consumer 和 Supplier 介绍。
  • Java8 内置的四大核心函数式接口 Consumer : 消费型接口 void accept(T t);Supplier : 供给型接口 T get(); Function@Test public void test01(){ this.con((e)-&amp;gt;System.out.println(&amp;quot...
  • 函数式接口、方法引用】

    千次阅读 2018-08-27 15:55:42
    01.第一章:函数式接口_概念及格式: 1).概念:有且只有一个抽象方法的接口。(可以同时有默认方法、静态方法...) 2).格式: 修饰符(public,默认) interface 接口名{ public abstract void show(); //可以同时有...
  • Java 8 函数式接口

    千次阅读 多人点赞 2019-01-08 23:06:50
    可以包含Object里所有能重写的方法,因为即使接口包含像String toString()这样的抽象方法,它的实现类也会因继承了Object类,而再次对接口中的toString()方法进行实现。  作用: 方便直接用Lambda表达式构造出...
  • 1、概述 有且仅有一个抽象方法的接口
  • 四大函数式接口

    千次阅读 2020-07-17 20:39:55
    在上一篇文章中Lambda表达式得好处与语法,我们知道使用Lambda表达式是需要使用函数式接口的,那么,岂不是在我们开发过程中需要定义许多函数式接口,其实不然,java8其实已经为我们定义好了4类内置函数式接口,这4...
  • 什么是函数式接口

    千次阅读 2020-06-02 12:17:17
    函数式接口就是只定义一个抽象方法的接口,如下例子: Java API中也有一些函数式接口 如Runnable接口类: Callable接口类: 同时只要接口只定义了一个抽象方法,那它就还是一个函数式接口,哪怕这个接口有好多...
  • Java的函数式接口简介

    千次阅读 2019-08-07 15:23:18
    所谓函数式接口,指的是只有一个抽象方法的接口。 函数式接口可以被隐式转换为Lambda表达式。 函数式接口可以用@FunctionalInterface注解标识。 JDK1.8之前就出现了一些符合函数式接口定义的接口: java.lang...
  • java8中常用函数式接口

    千次阅读 2019-09-28 23:36:47
    常用函数式接口 函数式接口 函数描述符 原始类型特化 Predicate T->boolean IntPredicate,DoublePredicate,LongPredicate Consumer T->void IntConsumer,LongConsumer,...
  • 详解JAVA8 函数式接口

    2020-08-18 18:33:57
    主要介绍了JAVA8 函数式接口的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
  • Java的函数式接口及使用示例

    千次阅读 2019-11-03 10:46:00
    Java 1.8加入了lambda表达式,可以进行函数式编程了,这几天也一直在研究这个东西,从Java核心技术中得知java.util.function包中已经定义了许多常用的函数式接口。 书中也列举了这个包下所有的接口,一共43个,有9...
  • JDK8函数式接口

    千次阅读 2019-05-12 11:54:34
    Supplier函数式接口 Consumer函数式接口 Function函数式接口 Predicate函数式接口
  • 必看:深入学习Java8中的函数式接口

    千次阅读 2018-06-06 21:47:38
    因为Java8引入了函数式接口,在java.util.function包含了几大类函数式接口声明。这里第一篇主要研究一下Function相关的接口。 FunctionalInterface注解 Java8的新引入,包含函数式的设计,...
  • 该注解只能标记在”有且仅有一个抽象方法”的接口上,表示函数式接口。 JDK8接口中的静态方法和默认方法,都不算是抽象方法。 接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中的方法,那么也不算...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 339,598
精华内容 135,839
关键字:

函数式接口