JDK1.8特性(上):函数式接口

Posted 码农里的蒸汽斯基

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK1.8特性(上):函数式接口相关的知识,希望对你有一定的参考价值。


晚来天欲雪,能饮一杯无?--李商隐

    

相信有的小伙伴面试中经常会问到JDK1.8的新特性有哪些(JDK1.8特性(上):函数式接口虽然现在喝咖啡的那个老头已经都把13上线了),最近在网上找了一些资料总结了一下,希望能给对这方面有需要的攻城狮们一些帮助.

JDK1.8特性(上):函数式接口

01

函数式接口的简单使用

JDK1.8特性(上):函数式接口

函数式接口:有且只有一个抽象方法的接口,称之为函数式接口,当然,接口中还可以包括其他的方法,比如默认方法,静态方法,私有方法,定义函数式接口可以用注解@FunctionalInterface,这样程序可以检测当前接口是否是一个函数式接口.函数式接口的使用一般是作为方法的参数和返回值

    话不多说,直接上代码JDK1.8特性(上):函数式接口

    先定义一个函数式接口:

@FunctionalInterface
public interface MyDemo {
    /**
     * 定义一个抽象方法
     */

    public abstract void myMethod();
}

     然后写一个Demo和主方法

public class Demo {


    /**
     * 定义一个方法,参数使用函数式接口MyDemo
     */

    public static void show(MyDemo demo) {
        demo.myMethod();
    }

    public static void main(String[] args) {
        //方式一:调用show方法,方法的参数是一个接口,所以可以传递接口的匿名内部类
        show(new MyDemo() {
            @Override
            public void myMethod()
{
                System.out.println("使用匿名内部类的方式...");
            }
        });

        //方式二:调用show方法,方法的参数是一个接口,可以使用lambda表达式
        show(() -> {
            System.out.println("使用lambda表达式的方式...");
        });

        //方式二可以简化为:
        show(() -> System.out.println("使用lambda表达式的方式..."));

    }

}

    当然也可以直接写一个实现该接口的类,然后以该实现类为参数

    这里使用内部类的话,会额外多加载一个class文件,但是lambda没有class的概念,所以会少加载一个class文件,对程序有一定的性能提升.


02

Lambda的延时加载

    有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费,而所有的lambda表达式是延迟执行的,即调用的时候,这正好可以作为解决方案,提升性能.

    比如:

     /**
     * 编写一个打招呼的方法
     * @param type 人员类型
     * @param msg 问候语
     */

    private static String greet(int type, String msg) {
        if (1 == type) {
            return msg;
        }else{
            return "告辞!";
        }
    }

    public static void main(String[] args) {
        String msg1 = "今天";
        String msg2 = "天气";
        String msg3 = "真好";

        String greet1 = greet(1, msg1 + msg2 + msg3);
        System.out.println(greet1);
    }

这里就涉及到一些浪费性能的问题,调用方法,传递的第二个参数是一个拼接后的字符串,程序会先把字符串拼接好,然后再调用方法,问题来了,如果传递的人员类型不是1,那么就不会使用如此拼接后的字符串,所以感觉字符串就白拼接了,存在了浪费


使用lambda优化:

首先创建一个函数式接口:

@FunctionalInterface
public interface MsgBuilder {

    //定义一个拼接消息的抽象方法,返回被拼接的消息
    public abstract String builderMsg();

}

然后编写demo:

/**
     * 编写一个打招呼的方法,参数传递人员类型和MsgBuilder接口
     * @param type 人员类型
     * @param mb 函数式接口
     */

    private static String greet1(int type, MsgBuilder mb) {
        //对人员类型进行判断,如果是1,则调用MsgBuilder接口中的builderMsg方法
        if (1 == type) {
            return mb.builderMsg();
        }else{
            return "告辞!";
        }
    }

测试:

public static void main(String[] args) {
        String msg1 = "今天";
        String msg2 = "天气";
        String msg3 = "真好";

        //调用打招呼方法,参数是一个函数式接口,所以可以传递一个lambda表达式
        String greet2 = greet1(1, () -> {
            return msg1 + msg2 + msg3;
        });

        //简化
        String greet3 = greet1(1, () -> msg1 + msg2 + msg3);

        System.out.println(greet2);
    }


这样用lambda表达式作为参数传递,只有满足条件时,人员类型是1时,才会调用接口中的拼接方法,如果条件不满足,那么就不会拼接,就不会造成性能浪费


    上述代码中会发现有的时候lambda表达式还会进一步简化,作者总结了一些简化的规律供大家参考:

简化规律

当只有一行代码的时候,如果有返回值,那么关键字return,大括号("{}")还有分号(";")都可以省略,有参数的话参数类型也可以省略

03

常用的函数式接口

    在JDK1.8中,函数式接口都在java.util.function包里面,下面讲几种常见的函数式接口.

Supplier接口

Supplier接口是一个生产型接口,里面仅包含一个无参的方法, T get(),用来获取指定类型的对象数据.指定接口的泛型是什么类型,接口中get方法就会生产什么类型的数据

代码:

public class SupplierDemo {

    public static String getName(Supplier<String> sup){
        return sup.get();
    }

    public static void main(String[] args) {
        String name = getName(() -> {
            return "Krupp";
        });
        //上述可简化为:
        String name1 = getName(()-> "Krupp");

        System.out.println(name+"是一位歌手");
        System.out.println(name1+"是一位歌手");
    }
}


Consumer接口

Comsumer接口是一个消费型接口,泛型指定什么类型,就可以使用其内的accept方法消费什么类型的数据,关于怎么消费,可以自定义逻辑

代码:

import java.util.function.Consumer;

public class ConsumerDemo {

    public static void consumerDemo(String name , Consumer<String> con){
        con.accept(name);
    }

    public static void main(String[] args) {
        consumerDemo("赵本山",(name)-> System.out.println(name+"是一位小品演员"));
    }
}

里面还有一个默认方法:andThen()

andThen()方法:其作用是可以链接两个Consumer接口,把两个接口链接到一起,再对数据进行消费

点进去看一下JDK源码,相关源码如下:

/**
 * 先判断第二个是否非空如果不是的话,就先执行当前对象的accept方法,然后,再执行第二个的accept方法
 */

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
}
/**
* requireNonNull方法只是用来判断非空的
*/

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

很好理解啦,对吧!!JDK1.8特性(上):函数式接口,

下面是demo:

import java.util.function.Consumer;

public class ConsumerDemo {

    /*需求:给定一个字符串,先将字符串进行大写,然后再小写
     *
     * */


    public static void consumerDemo(String name , Consumer<String> con1,Consumer<String> con2){
        //con1.accept(name);
        //con2.accept(name);
        con1.andThen(con2).accept(name);//和上面两行代码时相等的,谁先执行,谁就放前面
    }
    public static void main(String[] args) {
        consumerDemo("HelloWorld",
                (name)->{
            //先大写
                    System.out.println(name.toUpperCase());
                },
                (name)->{
            //再小写
                    System.out.println(name.toLowerCase());
                });
    }
}


Predicate接口

Predicate接口用于对某种数据类型的数据进行判断,其里面包含抽象方法test,接收一个参数,返回一个boolean值,符合条件则为true,不符合则返回false

代码如下:

public static boolean check(String s, Predicate<String> pre){
        return  pre.test(s);
    }

    /**
     * 调用
     */

    public static void main(String[] args) {
        boolean b = check("Hello", (s) -> s.length() == 5);
        System.out.println(b);
    }

里面有几个常用来判断的默认方法,and(),or(),negate(),相当于java中的逻辑运算符'&&','||','!'

使用方式如下:

public static boolean check(String s, Predicate<String> pre1){
        //return pre1.and(pre2).test(s); // return pre1.test(s) && pre2.test(s);
        //return pre1.or(pre2).test(s); // return pre1.test(s) || pre2.test(s);
        return  pre1.negate().test(s); // return !pre1.test(s)

    }


    /**
     * 调用
     */

    public static void main(String[] args) {
        boolean b = check("Hello", (s) -> s.length() == 5);
        System.out.println(b);
    }


Function接口

Function接口用来根据一个类型的数据得到另一个类型的数据,前者称之为前置条件,后者称为后置条件,里面的抽象方法  R apply(T t),根据T类型的参数获取类型R,至于如何获取,需要自定义逻辑

代码如下:

public static Integer changed(String str , Function<String,Integer> fun){
        Integer in = fun.apply(str);
        return in;
    }

    public static void main(String[] args) {
        String str = "123";
        changed(str,(s)-> Integer.valueOf(s));
    }

其里面还有一个默认方法:andThen(),用来进行组合操作,使用方法与上面的Consumer里面的andThen()方法一样,这里就不再赘述

如有错误欢迎指正

扫码查看更多内容



以上是关于JDK1.8特性(上):函数式接口的主要内容,如果未能解决你的问题,请参考以下文章

jdk1.8新特性——四大内置核心函数式接口

快来看看!!JDK1.8新特性之函数式接口

JDK1.8新特性之--函数式接口

jdk1.8新特性之lambda表达式

jdk1.8新特性(四大函数式接口与Stream流式计算)

Jdk1.8新特性之Lambda表达式