函数式接口

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

附注:语法糖-->指使用更加方便,但是原理不变的代码语法。例如遍历集合使用的for-each语法,底层实现原理就是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部 类的“语法糖”。

1.2语法
public interface MyFunctionalInterface {     void myMethod();      }  确保接口最终有且只有一个抽象方法。

1.3@FunctionalInterface注解 since(JDK8)
注意:即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。

1.4自定义函数式接口
对于函数式接口来说,典型使用场景就是作为方法的参数(或者作为返回值)

函数式编程
2.1Lambda的延迟执行
有些场景的代码执行后,结果不一定会被使用,从而造成性能的浪费。而LAMbda的延迟执行,可作为解决方案。

性能浪费的日志案例:

public static void logger(int level,String msg){
    if(level=1){
        System.out.println(msg);
    }
}

public static void main(String args[]){
    String msgA = "Hello";
    String msgB = "World";
    String msgC = "Java";
    //当执行到这个方法的时候,一定会进行字符串的拼接,不管level够不够
    logger(1,msgA+msgB+msgC); 
}

使用Lambda表达式进行优化:

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

public static void logger(int level,MessageBuilder builder){
    if(level=1){
        System.out.println(builder.buildMessage());
    }
}

pubblic static void main(){
    String msgA = "Hello";
    String msgB = "World";
    String msgC = "Java";
    //只是在此处创建接口抽象方法的方法体,真正执行是在level符合的时候
    //因此不会造成资源的浪费
    logger(1,()->msgA+msgB+msgC); 
}

验证:

//延迟执行的验证
pubblic static void main(){
    String msgA = "Hello";
    String msgB = "World";
    String msgC = "Java";
    
    logger(1,()->{
        System.out.println("执行了.....");
        return msgA+msgB+msgC;
    }); 
}

2.2接口(函数式接口)作为参数和返回值
Java中的Lambda表达式可以当做匿名内部类的替代品。如果方法的参数是一个接口,那么可以用lambda表达式替换。使用Lambda表达式作为参数,实际上就是使用函数式接口做为参数。因为:Lambda表达式就相当于该接口的一个匿名实现类(多态),也算是“子类”。

//函数式接口作为参数==>lambda表达式作为参数
public static void startThread(Runnable task){
    new Thread(task).start();
}
public static void main(String[] args){
    startThread(()->System.out.println("开始线程...."))
}
//lambda表达式作为返回值
public static Comparator<String> newComparator(){
    return (a,b)->b.length()-a.length();  //==》此处可以根据返回值推导类型
}

public static void main(String[] args){
    String[] arr ={aa,abc,abcd};
    System.out.println(Arrays.toString(arr));
    Arrays.sort(arr,newComparator);
    System.out.println(Arrays.toString(arr));
}

**本质上:Lambda表达就是接口的一个匿名的实现类。不管传入的参数或是返回值。


常见的函数式接口
JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。 
3.1Supplier接口
java.util.function.Supplier<T> 接口仅包含一个无参的方法:T get() 。用来获取一个泛型参数指定类型的对象数据。

public static void demoSupplier(Suppplier sup){
    retun sup.get();
}
public static void main(String[] args){
    String msgA = "Hello";
    String msgB = "World";
    //msgA+msgB相当于重写了接口中的抽象方法
    //整个Lambda表达式相当于一个该接口匿名实现类的对象
    System.out.println(demoSupplier(()->msgA+msgB)
); }
//求数组元素的最大值
public static void getMax(Suppplier sup){
    retun sup.get();
}
public static void main(String[] args){
    int[] arr = {15,2,55,20,3,0,36};
    
    int maxNum = getMax(()->{
        int max = arr[0];
        for(int i:arr){
            if(i>max){
                max = i;
            }
        }
        retun max;
    });
    System.out.println(maxNum);
}

3.2Comsumer接口
java.util.function.Consumer<T> 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据。其数据类型由泛型决定。 
抽象方法:accept
含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。

public class DemoConsumer{
    public static consume(Consumer con){
        con.accept("hello");
    }
    
    public static void main(String[] args){
        consume((s)-> s.toUpperCase());
    } 
}

默认方法:addThen
源码:

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

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

原理:
return (T t)->{accept(t);after.accept()}新的一个==》new Consumer<T t>{ @override accept(T t){accept(t);after.accept(t)}};
==》如果过再加上一个andThen newConsumer<T t>{@override accept(T t){  accept(t); three.accept(t) }}
此时:accept(t);-->相当于accept(T t){accept(t);after.accept(t)}    ==》嵌套的关系

public class DemoConsumer2{
    public static consume(String info,Consumer con1){
        con1.andThen(con2).add(con3).accept(info);
    }
    
    public static void main(String[] args){
        consume(s,s->System.out.println(s.toUpperCase()), 
        s->System.out.println(s.toLowerCase()),s->System.out.println(s.toUpperCase()));
    } 
}

案例:

public calss DemoConsumer3{
    public getInfo(String[] arr,Consumer con1,Consumer con2){
        for(String str:arr){
            con1.andThen(con2).accept(str);
        }        
    }
    public static void main(String[] args){
        String[] arr = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男"};
        getInfo(arr,s->System.out.println("姓名:"+s.split(",")[0]),
                        s->System.out.println("性别:"+s.split(",")[1]))
    }
}

3.3Predicate接口
java.util.function.Predicate<T> 需要对某种类型的数据进行判断,从而得到一个boolean值结果。
抽象方法:boolean test(T t) 。用于条件判断的场景。
默认方法:and  or negate
执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在 test 方法调用之前 调用 negate 方法,正如 and 和 or 方法一样。

3.4Function接口
java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。 
抽象方法:apply
默认方法:andThen

 

posted @ 2018-11-16 21:39  payn  阅读(270)  评论(0)    收藏  举报