java

JAVA8新特性-函数式接口和lambda表达式

2019-12-07  本文已影响0人  源码互助空间站

一 前言

JAVA8很早就出现了,其中有些新特性值得关注。现在之所以这么关注JAVA8是因为公司JDK整体都升级为8了,所有在这里进行一些基础到项目改造的记录过程,以此共勉。

二 函数式编程

函数式编程是JAVA8的心特性了。

什么是函数式接口?
1 函数式接口也是java中的一个普通接口
2 只包含一个接口方法的特殊接口
3 有语义化注解@FunctionalInterface

2.1 函数式接口入门demo

@FunctionalInterface
public interface UserService {

    /**
     * 函数式接口是Java系统的接口,
     *  只包含一个接口方法的特殊接口,
     *  需要注解@FunctionalInterface进行语义化检查
     * @param user
     * @return
     */
    String User(String user);

函数式接口使用上跟普通接口的使用是一样的。

2.2函数式接口之默认方法

默认方法是JAVA8的新特性,在8之前接口内默认的都是public abstract方法,不允许出现有具体的方法。

@FunctionalInterface
public interface UserService {

    /**
     * 函数式接口是Java系统的接口,只包含一个接口方法的特殊接口,需要注解@FunctionalInterface进行语义化检查
     * @param user
     * @return
     */
    String User(String user);

    /**
     * java8新特性:default方法,这里是给实现类提供的功能扩展(公用方法)
     * @param user
     * @return
     */
    default String checkUser(String user) {
        if(user != null){
            System.out.println("----check not null");
        }else {
            System.out.println("----check null");
        }
        return user;
    }
}

使用默认方法的代码

public class Test {
    public static void main(String[] args) {
        //1、测试函数式编程接口
        UserServiceImpl userService = new UserServiceImpl();
        String admin = userService.User("admin");
        System.out.println("---"+admin);

        //2、测试default方法
        UserServiceImpl userService1 = new UserServiceImpl();
        String admin1 = userService1.checkUser("admin");
        System.out.println("----"+admin1);
    }
}

默认方法的作用:
默认方法是提供公用的方法,所有的函数式接口的实现类均可以调用这个方法。

2.3 函数式接口之静态方法

/**
     * java8新特性:static方法,这里是给实现类提供的功能扩展(公用方法)
     * @param user
     * @return
     */
    static String checkUserStatic(String user) {
        if(user != null){
            System.out.println("---- checkUserStatic check not null");
        }else {
            System.out.println("----checkUserStatic check null");
        }
        return user;
    }

测试代码

public class Test {
    public static void main(String[] args) {
        //1、测试函数式编程接口
        UserServiceImpl userService = new UserServiceImpl();
        String admin = userService.User("admin");
        System.out.println("---"+admin);

        //2、测试default方法
        UserServiceImpl userService1 = new UserServiceImpl();
        String admin1 = userService1.checkUser("admin");
        System.out.println("----"+admin1);

        //3、测试静态方法
        String admin2 = UserService.checkUserStatic("admin");
        System.out.println("----" + admin2);
    }
}

静态方法作用:
和默认方法一样都是为接口做扩展功能使用的。
但是默认方法是用过实现类调用的,而静态方法可以直接过接口调用。

2.4 小细节

前面说过函数式接口只能有一个接口方法。如果包含多个会出现错误提示。 不符合函数式接口规范的提示.png

但是还是要注意,因为所有的类都是继承自Object,所有来自Objtect的方法是不受影响的。比如重写toString()方法是可以通过的。


Object方法.png 这些方法还包括: 图片.png

三 jdk8提供的内置函数式接口

jdk8在java.util.function提供了大量函数式接口,覆盖了很多我们需要使用的场景

jdk8提供的内置函数式接口.png

我们可以看出,报名是java.util.function;并且有@FunctionalInterface注解,有且只有一个方法accept(T t,U u);

3.1 Predicate 传入T对象,返回Boolean类型结果

Predicate<T> 介绍参数T对象,返回一个Boolean类型结果,适合校验类型的接口

         /**
         * 1、Predicate<T> 介绍参数T对象,返回一个Boolean类型结果,适合校验类型的接口
         */
        Predicate<String> predicate = (String username)->{
            return "admin".equals(username);
        };
        System.out.println(predicate.test("admin"));//true
        System.out.println(predicate.test("manager"));//false

3.2 Consumer 接受参数T类型,没有返回值

Consumer 接受参数T类型,没有返回值,场景:消息发送

        /**
         * 2、Consumer 接受参数T类型,没有返回值
         * 场景:消息发送
         */
        Consumer<String> consumer = (String messge)->{
            System.out.println("--send message--" + messge);
        };
        consumer.accept("hello world");//--send message--hello world

3.3 Function接收T对象,返回R对象

        /**
         * 3、Function接收T对象,返回R对象
         */
        Function<String,String> function = (String username)->{
            return username.equals("admin")?"admin 用户":"manager 用户";
        };
        System.out.println(function.apply("admin"));//admin 用户
        System.out.println(function.apply("1234"));//manager 用户

3.4 Supplier不接收参数,直接返回R对象

Supplier不接收参数,直接返回R对象,适合类似产生随机数场景

        /**
         * 4、Supplier不接收参数,直接返回R对象
         * 场景:UUID
         */
        Supplier<String> supplier = ()->{
            return UUID.randomUUID().toString();
        };
        System.out.println(supplier.get());//fab43f66-eeaf-49d6-a9e4-956d9aa21dbd
        System.out.println(supplier.get());//38a25978-a3fd-4f48-99f7-5f32624c31c9
        System.out.println(supplier.get());//c07d5c6d-97be-4ee3-a650-0cc857e5bb9b

3.5 UnaryOperator接收T对象,执行业务处理后返回更新后的T对象

UnaryOperator接收T对象,执行业务处理后返回更新后的T对象,适合对对象处理

      /**
         * 5、UnaryOperator接收T对象,执行业务处理后返回更新后的T对象
         * 适合对对象处理
         */
        UnaryOperator<String> unaryOperator = (String username)->{
            return username + "new";
        };
        System.out.println(unaryOperator.apply("me"));//menew

3.6 BinaryOperator接收两个T对象,执行业务处理后返回一个T对象

BinaryOperator接收两个T对象,执行业务处理后返回一个T对象,场景:返回比赛结果

        /**
         * 6、BinaryOperator接收两个T对象,执行业务处理后返回一个T对象
         * 场景:返回比赛结果
         */
        BinaryOperator<Integer> binaryOperator = (Integer i1,Integer i2) ->{
            return i1>i2?i1:i2;
        };
        System.out.println(binaryOperator.apply(1,2));//2
        System.out.println(binaryOperator.apply(3,2));//3

感受到内置函数式接口的作用了吗,几乎包含了我们所有的场景

四 lambda表达式

4.1 lambda表达式入门demo

        //4 、匿名内部类
        UserService userService2 = new UserService() {
            @Override
            public String User(String user) {
                System.out.println("=========="+user);
                return "admin".equals(user)?"admin":"manger";
            }
        };
        String user = userService2.User("admin");
        System.out.println("------"+user);

        //5、 lambda表达式
        UserService userService3 = (String user2) -> {
            return "admin".equals(user2)?"lambdaadmin":"lambdamanger";
        };
        System.out.println(" lambda---" + userService3.User("admin2"));

匿名内部类和lambda表达式实现的功能是一样的。但明显代码精简了很多,之保留了数据处理的部分。

4.2 lambda表达式基本语法

1、声明:lambda表达式绑定的接口类型
2、参数:包含在一对()括号内,和绑定接口中的抽象方法的参数个数和顺序一致
3、操作符:->
4、执行代码块:包含在一对{}内,出现在操作符的右侧
[接口声明]=(参数)-> {执行代码块};

下面给出各种情况下的基本用法。

   //1 没有参数,没有返回值
    interface Ilambda1{
        void test();
    }
    //2 有参数,没有返回值
    interface Ilambda2{
        void test(String u,Integer i);
    }
    //3 有参数,有返回值
    interface Ilambda3{
        int test(int i,int y);
    }

4.2.1 没有参数没有返回值

      Ilambda1 i1= ()->{
            System.out.println("hello world");
        };
        i1.test();

高级用法:如果返回值只有一行,可以省去{}


        Ilambda1 i2= ()-> System.out.println("hello world");
        i2.test();

4.2.2 有参数没有返回值

 Ilambda2 i3= (String u,Integer i)->{
            System.out.println(u+" say he is " + i + "years old");
        };
        //tom say he is 18years old
        i3.test("tom",18);

高级用法,传参可以省去类型,执行时JVM会自动去匹配类型

Ilambda2 i4= (u,i)->{
            System.out.println(u+" say he is " + i + " years old");
        };
        //jack say he is 18years old
        i3.test("jack",18);

4.2.3 有参数有返回值

 Ilambda3 i5 = (i,j)->{
            return i+j;
        };
        //300
        System.out.println(i5.test(100,200));

高级用法:如果处理数据只有一行代码,可以省去{}

  Ilambda3 i6 = (i,j)-> i+j;
        //400
        System.out.println(i6.test(200,200));

lambda表达式基本语法总结:
1、lambda表达式,必须和接口进行绑定 2、lambda表达式的参数,可以附带0到n个,括号中的参数类型可以不指定,jvm在运行时,会自动根据绑定抽象方法匹配 3、lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果会自动返回,如果添加了大括号,或者有多行代码,必须通过return关键字返回执行结果

4.3 lambda表达式变量捕获

lambda表达式内对变量的使用和原来稍有区别。

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        test.testInnerClass();
        test.testLambda();

    }
    String s1 = "全局变量";
    //匿名内部类对变量的访问
    public void testInnerClass(){
        String s2 = "局部变量";

        new Thread(new Runnable() {
            String s3 = "内部变量";
            @Override
            public void run() {
                //this表示当前内部类型的对象
                //System.out.println(this.s1);
                System.out.println(s1);

                //局部变量的访问,不能对局部变量进行修改,默认final类型
                System.out.println(s2);
                //s2 = "123";

                System.out.println(s3);
                System.out.println(this.s3);
            }
        }).start();


    }
    // lambda表达式获取变量
    public void testLambda(){
        String s2 = "局部变量Lambda";
        new Thread(()->{
            String s3 = "内部变量Lambda";

            //this表示当前方法所在类型的对象
            System.out.println(this.s1);
            System.out.println(s1);

            //局部变量的访问,不能对局部变量进行修改,默认final类型
            System.out.println(s2);
            //s2 = "123";

            s3 = "内部变量Lambda new";
            System.out.println(s3);
        }).start();
    }
}

变量访问总结:
lambda表达式匿名内部类优化了this关键字,不在单独建立对象作用域,表达式本身就是所属类型的对象,在语法语义上更加简洁。

4.4 lambda表达式方法重载的影响

这里需要注意,在前面的例子中还没有涉及方法重载的问题,lambda表达式方式会自动匹配类型(传参可以省去类型,执行时JVM会自动去匹配类型),但是重载的需要注意类型转换。

public class Test {
    /**
     * 定义两个接口,里面方法名一样
     */
    @FunctionalInterface
    interface Param1{
        void outInfo(String info);
    }
    @FunctionalInterface
    interface Param2{
        void outInfo(String info);
    }

    //定义重载方法
    public void lambdaMethod(Param1 param1){
        param1.outInfo("--param1--");
    }
    public void lambdaMethod(Param2 param2){
        param2.outInfo("--param2--");
    }

    public static void main(String[] args) {
        Test test = new Test();
        //匿名内部类方式
        test.lambdaMethod(new Param1() {
            @Override
            public void outInfo(String info) {
                System.out.println(info);
            }
        });

        test.lambdaMethod(new Param2() {
            @Override
            public void outInfo(String info) {
                System.out.println(info);
            }
        });
        //lambda表达式方式,这里需要类型提示
        test.lambdaMethod((Param1) (info)->{
            System.out.println(info + "--lambda--");
        });
        //lambda表达式方式,这里需要类型提示
        test.lambdaMethod((Param2) (info)->
            System.out.println(info + "--lambda--")
        );
    }
}

5 小结

这篇仅仅是入门级的lambda使用,后续会完成lambda表达式在集合中的使用。

上一篇 下一篇

猜你喜欢

热点阅读