java-函数式编程-函数式接口以及方法应用

一、lambda表达式

 

    1、 Lambda表达式是匿名内部类的简化写法。

    Lambda标准格式:

    (参数类型 参数名) -> {

        方法体;

        return 返回值;    

    }

 

    省略规则:

        1. 小括号中的参数类型可以省略。

        2. 如果小括号中只有一个参数,那么可以省略小括号

        3. 如果大括号中只有一条语句,那么可以省略大括号,return,以及;

    使用前提:

        1. 必须有接口, 接口中有且仅有一个需要被重写的抽象方法。

        2. 必须支持上下文推导。 要么有接口作为参数,要么有一个变量接收这个Lambda表达式.

    函数式编程: 可推导就是可省略

 

注:此处省略了persion对象的代码

public class Demo01Lambda {

    public static void main(String[] args) {

        //使用Lambda表达式实现多线程

        new Thread(() -> {

            System.out.println("Lambda标准格式执行多线程");

        }).start();

 

        //使用Lambda省略格式实现多线程

        new Thread(() -> System.out.println("Lambda省略格式执行多线程")).start();

    }

}

 

package cn.itcast.demo01;

 

import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

 

/*

    创建集合,保存Person,并且使用比较器排序方式对集合中的Person对象按照年龄升序排序

*/

public class Demo02Lambda {

    public static void main(String[] args) {

        //创建集合

        ArrayList<Person> list = new ArrayList<>();

        //添加Person

        list.add(new Person("大幂幂", 30));

        list.add(new Person("王思聪", 28));

        list.add(new Person("王健林", 40));

 

        //使用比较器排序对集合中的Person按照年龄升序排序

        /*

        Collections.sort(list, new Comparator<Person>() {

            @Override

            public int compare(Person o1, Person o2) {

                return o1.getAge() - o2.getAge();

            }

        });

        */

 

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

 

 

        //打印集合

        System.out.println(list);

    }

}

 

二、函数式接口

 

    1、概念:如果一个接口中有且仅有一个需要被重写的抽象方法,那么这个接口就是函数式接口。

    2、函数式接口的使用:

        (1)当做一个普通接口去使用(给其他类实现)。

        (2)当做 lambda表达式的使用前提去使用(使用lambda表达式必须的使用函数式接口)。

有一个注解叫做:@ Functionallnterface,真个注解可以验证是否是函数式接口,这个注解在函数的上面,如果某个接口不是函数式接口,那么加上这个注解会报错。

   这个注解仅仅用来验证一个接口是否是一个函数式接口,如果没有这个注解,只要某个接口满足函数式接口的条件,那么这个接口就是一个函数式接口。

@FunctionalInterface

public interface MyInterface {

    void method();

}

请定义一个函数式接口 Eatable ,内含抽象 eat 方法,没有参数或返回值。使用该接口作为方法的参数,并进而

    通过Lambda来使用它

 

public interface Eatable {

    //eat方法

    void eat();

}

 

 

public class Demo01LambdaTest {

    public static void main(String[] args) {

        method(() ->  System.out.println("使用Lambda表达式吃饭饭,香香哒"));

    }

 

    //参数是一个函数式接口。

    //参数如果是函数式接口,那么可以传递Lambda表达式

    public static void method(Eatable eatable) {

        eatable.eat();

    }

}

 

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

 

@FunctionalInterface

public interface Sumable {

    int sum(int a, int b);

}

 

 

public class Demo02LambdaTest {

    public static void main(String[] args) {

 

        method(new Sumable() {

            @Override

            public int sum(int a, int b) {

                return a + b;

            }

        });

 

        //调用method方法,传递Lambda表达式

        method((a, b) -> a + b);

    }

 

    public static void method(Sumable sumable) {

        int result = sumable.sum(10, 20);

        System.out.println("result:" + result);

    }

}

三、使用函数式接口打印日志,优化性能浪费问题

    (1)传统方式打印日志

    步骤:

        1. 定义了三个字符串

        2. 对这三个字符串进行了拼接,并且当做参数传递给了printLog方法。

        3. 进行判断,如果日志等级满足要求,那么就打印日志信息。

                    如果日志等级不满足要求,那么就不打印。

    如果不满足要求,那么就不会打印日志信息,但是之前已经把日志信息拼接好了,拼接好之后却没有使用

    这样就产生了性能浪费。

public class Demo01Log {

    public static void main(String[] args) {

        //定义三个字符串

        String s1 = "hello";

        String s2 = "world";

        String s3 = "java";

 

        //调用printLog方法,打印日志信息

        printLog(1, s1 + s2 + s3);

    }

 

    //用来打印日志信息

    //参数给两个,一个是日志等级。 第二个参数是日志信息

    public static void printLog(int level, String msg) {

        //进行判断,如果日志等级是3,那么就打印日志信息

        if (level == 3) {

            System.out.println(msg);

        }

    }

}

 

(2)使用Lambda表达式解决性能浪费的问题

    如果日志等级满足要求,那么就进行拼接,然后打印这个日志信息。

    如果日志等级不满足要求,那么就不拼接了。

    Lambda表达式是作为接口中抽象方法的内容存在的。 所以当通过接口调用抽象方法时,才会

    执行对应的Lambda表达式。

public class Demo02LogLambda {

    public static void main(String[] args) {

        //创建三个字符串

        String s1 = "hello";

        String s2 = "world";

        String s3 = "java";

 

        //调用method方法,根据日志等级打印日志信息

        method(3, () -> {

            String msg = s1 + s2 + s3;

            System.out.println(msg);

        });

    }

 

    //第一个参数为日志等级,第二个参数为用来打印日志的函数式接口。

    public static void method(int level, MessageBuilder messageBuilder) {

        if (level == 1) {

            messageBuilder.printMsg();

        }

    }

}

 

.//定义一个函数式接口

public interface MessageBuilder {

    void printMsg();

}

 

四、Lambda表达式的六种使用方式

1、lambda表达式作为方法参数

 

//lambda表达式做为方法参数

 

public class Demo01LambdaParam {

    public static void main(String[] args) {

        new Thread(() -> System.out.println("使用Lambda表达式执行了线程")).start();

    }

}

 

2、Lambda表达式作为返回值类型

 

/*

    Lambda表达式作为返回值

*/

public class Demo02LambdaReturn {

    public static void main(String[] args) {

        //创建集合

        ArrayList<Person> list = new ArrayList<>();

        //调用list的add方法,向集合中添加Person对象

        list.add(new Person("灭绝师太", 50));

        list.add(new Person("金花婆婆", 20));

        list.add(new Person("金毛狮王", 30));

 

        //使用比较器排序,对学生根据年龄升序排序

        Collections.sort(list, getComparator());

 

        //打印结果

        System.out.println(list);

    }

 

    public static Comparator<Person> getComparator() {

        return (o1, o2) -> o1.getAge() - o2.getAge();

 

        /*

        return new Comparator<Person>() {

            @Override

            public int compare(Person o1, Person o2) {

                return o1.getAge() - o2.getAge();

            }

        };

        */

    }

}

 

(1) 请自定义一个函数式接口 MySupplier ,含有无参数的抽象方法 get 得到 Object 类型的返回值。并使用该函数式

    接口分别作为方法的参数和返回值。

 

public class Demo03LambdaTest {

    public static void main(String[] args) {

        //调用method方法,传递Lambda表达式

        //method(() -> "abc");

 

        method(getMySupplier());

    }

 

    public static void method(MySupplier mySupplier) {

        Object obj = mySupplier.get();

        System.out.println(obj);

    }

 

    //Lambda表达式作为方法的返回值.

    public static MySupplier getMySupplier() {

        return () -> "hello";

    }

}

 

public interface MySupplier {

    //没有参数,有一个Object类型的返回值

    Object get();

}

 

public interface MySupplier{

       return get();

}

五、方法引用

1、  Lambda表达式做的事情是拿到参数之后, 【直接】对参数进行了打印

    此时Lambda表达式做的事情就和方法做的事情重叠了。

    Lambda表达式拿到参数之后直接进行了打印, 这样的话其实就没有必要再写一遍参数了, 因为这个内容可以推导。

    如果Lambda表达式拿到参数之后,直接去干了某些事情,那么可以写成另一种方式,另一种方式是Lambda的孪生兄弟: 方法引用

 

方法引用是Lambda表达式的一种简化方式

 

 方法的使用格式     :: 两个冒号

 

public class Demo01Lambda {

    public static void main(String[] args) {

        //lambda表达式, 方法参数多余了,因为拿到参数之后直接对参数进行了打印

        method(str -> System.out.println(str));

 

        //方法引用

        //含义, 拿到参数之后,直接使用System.out的println方法打印这个参数.

        method(System.out::println);

    }

 

    public static void method(Printable printable) {

        printable.print("hello");

    }

}

 

public class Demo02Lambda {

    public static void main(String[] args) {

        //调用method方法,传递一个Lambda表达式

        method(num -> System.out.println(num));

 

        //换成方法引用

        method(System.out::println);

    }

 

    public static void method(PrintNumberable printNumberable) {

        printNumberable.printNumber(10);

    }

}

 

public interface Printable {

    void print(String str);

}

 

 

 

    public interface PrintNumberable {

    void printNumber(int num);

}

 

 

2、方法 引用的六种方式

   对于类来说有四种: 

(1)对象引用成员方法

(2)类名引用静态方法

(3)supper应用父类方法

(4)this引用本类方法

对于构造器来说有两种方式

(1)类的构造器引用

(2)数组的构造器引用

 

1、对象引用成员方法: 

   格式;对象名::方法名

 

public class Demo01MethodRef {

    public static void main(String[] args) {

        //调用method方法,传递Lambda表达式。 将参数字符串转成大写并打印

        //method(str -> System.out.println(str.toUpperCase()));

 

        MyClass myClass = new MyClass();

 

        //method(str -> myClass.printUpperStr(str));

        //换成方法引用

        //拿到参数之后,直接调用myClass对象的printUpperStr方法去对这个参数进行处理。

        method(myClass::printUpperStr);

    }

 

    //参数是函数式接口,调用的时候可以传递Lambda表示

    public static void method(Printable printable) {

        printable.print("hello");

    }

}

 

 

public class Demo02MethodRef {

    public static void main(String[] args) {

        //调用method方法,传递Lambda表达式

        //method(fileName -> System.out.println("Lambda表达式在七手八脚的处理这个文件:" + fileName));

 

        //创建一个助手对象

        Assistant assistant = new Assistant();

 

        //method(fileName -> assistant.doFile(fileName));

        //方法引用

        method(assistant::doFile);

    }

 

    public static void method(Helper helper) {

        helper.help("工资流水.txt");

    }

}

 

public interface Helper {

    //帮助处理文件

    void help(String fileName);

}

 

 

public class MyClass {

    //定义方法,接收一个字符串的参数,并且将这个字符串转成大写打印

    public void printUpperStr(String str) {

        System.out.println(str.toUpperCase());

    }

}

 

 

public interface Printable {

    void print(String str);

}

 

 

2、类名引用过成员方法

        格式   类名::静态方法

 

public class Demo01MethodRef {

    public static void main(String[] args) {

        //调用method方法,传递Lambda表达式

        //method(num -> num > 0 ? num : -num);

 

        //Math数学工具类中有一个abs方法,可以直接求出一个数的绝对值

        method(num -> Math.abs(num));

 

        //拿到参数num,直接调用Math类的静态方法abs对参数num进行了处理。

 

        method(Math::abs); //表示拿到参数之后,对参数直接通过Math类的abs方法进行处理。

    }

 

    public static void method(Calcable calcable) {

        int result = calcable.abs(-10);

        System.out.println(result);

    }

}

 

/*

    类名引用静态方法的练习

*/

public class Demo02MethodRef {

    public static void main(String[] args) {

        //调用method方法,传递Lambda表达式,检查参数字符串是否为空(如果这个字符串是null,或者是一个"",那么就是空)

        //如果字符串是空,那么就返回true。不是空返回false

        //method(str -> str == null || str.equals(""));

 

        method(str -> StringUtils.isBlank(str));

 

        //改成方法引用

        method(StringUtils::isBlank); //拿到参数直接对参数使用StringUtils的静态方法isBlank进行处理。

    }

 

    public static void method(Checker checker) {

        boolean flag = checker.check("");

        System.out.println("flag:" + flag);

    }

}

 

    3、super引用成员方法

        格式  super ::方法名

public class Demo01MethodRef {

    public static void main(String[] args) {

        //创建一个Student对象

        Student stu = new Student();

        stu.sayHello();

    }

}

 

@FunctionalInterface

public interface Greetable {

    void greet();

}

 

 

public class Person {

    public void sayHello() {

        System.out.println("雷猴");

    }

}

 

 

public class Student extends Person{

 

    public void sayHello() {

        //method(() -> System.out.println("雷猴"));

 

        //method(() -> super.sayHello());

 

        //改成方法引用。 使用super调用了父类的方法。 所以格式为: super::父类方法

        method(super::sayHello);

 

    }

 

    //定义方法,方法参数传递一个函数式接口

    public void method(Greetable greetable) {

        greetable.greet();

    }

}

 

 

4、this引用本类方法

public class Demo01MethodRef {

    public static void main(String[] args) {

        Man man = new Man();

        man.beHappy();

    }

}

 

public class Man {

 

    public void buyGift() {

        System.out.println("买一个500平米的大house");

    }

 

    public void marry(Richable richable) {

        richable.buy();

    }

 

    public void beHappy() {

        //marry(() -> System.out.println("买了一个500克拉的大钻戒"));

        //marry(() -> buyGift());

 

        //使用this引用本类方法。  this::本类方法

        marry(this::buyGift);

    }

}

 

 

 

public interface Richable {

    void buy();

}

 

 

5、类的构造器使用

        格式  类名:: new 

public class Demo01MethodRef {

    public static void main(String[] args) {

        //调用method方法。 传递Lambda表达式

        //拿到参数姓名和年龄之后直接通过构造方法根据姓名和年龄创建了Person对象.所以可以换成方法引用.

        method((name, age) -> new Person(name, age));

 

        //使用类的构造器引用, 因为之前代码是在使用构造方法创建对象

        method(Person::new); //含义,拿到参数之后直接使用这些参数通过构造方法创建对象

    }

 

    //函数式接口当做方法参数

    public static void method(PersonBuilder personBuilder) {

        Person p = personBuilder.createPerson("大幂幂", 30);

        System.out.println(p);

    }

}

 

    6、数组的构造器引用。

 

    格式:

        数据类型[]::new

public class Demo02MethodRef {

    public static void main(String[] args) {

        //调用method方法,传递lambda表达式,参数是多少,就创建一个长度是多少的数组然后返回

        //method(len -> new int[len]); //拿到参数len之后直接根据参数类创建了一个int数组。所以可以换成方法引用

 

        method(int[]::new); //拿到参数后,直接根据参数创建了一个int数组

 

    }

 

    //参数传递函数式接口

    public static void method(ArrayBuilder arrayBuilder) {

        int[] arr = arrayBuilder.createArray(10);

        System.out.println(Arrays.toString(arr));

    }

}

 

 

public interface PersonBuilder {

    //给我姓名和年龄,那么我就可以根据这个姓名和年龄给你一个Person对象。

    Person createPerson(String name, int age);

}

 

 

 

public interface ArrayBuilder {

    //给我长度,我就可以给你对应长度的int数组

    int[] createArray(int len);

}

 

 

package cn.itcast.demo10;

 

public class Person {

    private String name;

    private int age;

 

    @Override

    public String toString() {

        return "Person{" +

                "name='" + name + '\'' +

                ", age=" + age +

                '}';

    }

 

    public Person() {

    }

 

    public Person(String name, int age) {

 

        this.name = name;

        this.age = age;

    }

 

    public String getName() {

 

        return name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

    public int getAge() {

        return age;

    }

 

    public void setAge(int age) {

        this.age = age;

    }

}

 

 

六、Java提供的函数式接口

在jdk8之后,多了一个java.util.function包。里面提供了大量的函数式接口。

    其中有一个接口,叫做Supplier,可以看成一个生产者,里面的get方法,可以获取一个对象

    Supplier<T>  泛型表示要获取的对象的数据类型。

    抽象方法:

        T get() 获取一个对象

public class Demo01Supplier {

    public static void main(String[] args) {

        //调用method方法。参数是一个函数式接口,可以传递Lambda表达式

        method(() -> "hello");

    }

 

    //定义一个method方法,把函数式接口Supplier当做方法的参数

    public static void method(Supplier<String> supplier) {

        String str = supplier.get();

        System.out.println(str);

    }

}

 

 

/*

    使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用

    java.lang.Integer 类。

*/

public class Demo02SupplierTest {

    public static void main(String[] args) {

        //定义一个int数组

        int[] arr = {5, 10, -20, 3, 8};

        method(() -> {

            //在Lambda表达式大括号中,求出数组的最大值,然后返回即可

 

            //定义参照物max用于表示每次比较的最大值。 最开始可以把数组中索引为0的元素当成最大的赋值给这个变量

            int max = arr[0];

            //拿数组中其他的每一个元素和这个max进行比较,如果比max大,那么就把这个值赋值给max

            for(int i = 1; i < arr.length; i++) {

                if (arr[i] > max) {

                    max = arr[i];

                }

            }

            //求出结果之后,返回最终结果

            return max;

        });

    }

 

    public static void method(Supplier<Integer> supplier) {

        //调用Supplier的get方法获取一个结果

        Integer result = supplier.get();

        System.out.println("result:" + result);

    }

}

 

2、 在java中,还有一个函数式接口叫做Consumer,可以看成一个消费者,用来消费一个数据(使用这个数据去做一些事情)。

    Consumer<T> 泛型T,表示要消费数据的数据类型。

    抽象方法:

        void accept(T t): 表示要对参数t进行消费。(拿到参数t之后去做一些事情)

public class Demo03Consumer {

    public static void main(String[] args) {

        //调用method方法, 传递一个Lambda表达式

        //method(s -> System.out.println(s));

 

        //方法引用的写法

        method(System.out::println);

    }

 

    //定义方法,把函数式接口Consumer当做参数

    public static void method(Consumer<String> consumer) {

        consumer.accept("hello");

    }

}

 

/*

    在函数式接口Consumer中有一个默认方法,叫做andThen,可以将两个Consumer合并(拼接)成一个Consumer对象

 

    default Consumer andThen(Consumer after): 将两个Consumer进行合并。比如a.andThen(b)。 是对a和b进行合并。并且有先后顺序是先a后b

 

*/

public class Demo04Consumer {

    public static void main(String[] args) {

        method(s -> System.out.println(s.toUpperCase()),

                s -> System.out.println(s.toLowerCase()));

    }

 

    /*

        定义方法,要两个Consumer

        第一个Consumer用来将Hello全部转成大写并打印

        第二个Consumer用来将Hello全部转成小写并打印

     */

    public static void method(Consumer<String> one, Consumer<String> two) {

        //分别消费处理Hello

        //one.accept("Hello");

        //two.accept("Hello");

 

        //将one的内容和two的内容合并

        //合并之后的three里面包含的one和two的内容, 先one后two。

        //Consumer<String> three = one.andThen(two);

        //调用three的accept方法,处理Hello

        //three.accept("Hello"); //因为three中包含了one和two的内容,所以这句话相当于 one.accept("Hello")  two.accept("Hello")

 

        //将one和two合并之后直接调用accept方法处理这个字符串

        one.andThen(two).accept("Hello"); //先通过one调用accept,再通过two调用accept

    }

}

 

/*

    下面的字符串数组当中存有多条信息,请按照格式“ 姓名:XX。性别:XX。 ”的格式将信息打印出来。要求将打印姓

    名的动作作为第一个 Consumer 接口的Lambda实例,将打印性别的动作作为第二个 Consumer 接口的Lambda实

    例,将两个 Consumer 接口按照顺序“拼接”到一起

 

    打印数组中的内容,要使用两个Consumer进行打印, 第一个Consumer打印姓名,第二个Consumer打印性别.

    还要将这两个Consumer合并成一个Consumer

 

 

    打印内容

        姓名:迪丽热巴。 性别:女

        姓名:古力娜扎。 性别:女

        姓名:马尔扎哈。 性别:男

*/

public class Demo05ConsumerTest {

    public static void main(String[] args) {

        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" };

        //调用method方法,进行处理

        method(str -> System.out.print("姓名:" + str.split(",")[0]),

                str -> System.out.println("。 性别:" + str.split(",")[1]),

                 array);

    }

    /*

        参数one用来处理数组中每个元素的姓名

        参数two用来处理数组中每个元素的性别

        参数arr表示要处理的数组

     */

    public static void method(Consumer<String> one, Consumer<String> two, String[] arr) {

        //遍历数组,拿到数组中的每个元素

        for(String str : arr) {

            //使用one处理打印姓名

            //one.accept(str);

            //使用two处理打印性别

            //two.accept(str);

 

            //将one和two进行合并,然后调用accept方法

            one.andThen(two).accept(str);

        }

    }

}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值