Lambda表达式-四大函数式接口

2020-09-08  本文已影响0人  徒手說梦话

Java语言自发布以来,从Java1.1一直到现在的Java14,Java通过不停的增加新功能,使得这门语言不断得到良好的提升。

其中比较具有重要里程碑的版本,如JDK5.0,提供给我们诸如增强for循环,可变参数列表,静态导入,枚举类型定义,自动装箱拆箱,泛型等一些列优秀的功能。

以及后来的jdk7.0,提供犹如二进制字面量,数字常量下划线,Switch运算中String类型的引入,try-with-resource的资源自动释放等,都给我们带来了很方便的操作和极大的便利。

接口方法

JDK1.8之前,接口中的方法,都必须是抽象方法。实现接口的类,必须实现接口中定义的每一个抽象方法。

由于JDK1.8的API,在已有的接口上,新增了很多的新方法,这种新增变化带来的问题,正如上述的情况一样,也会给使用这些接口的老用户带来很多的不便。

为了解决这个问题,JDK1.8中引入了一种新的机制:接口可以支持在声明方法的同时,提供实现。

主要通过两种方式可以完成这种操作:

  1. 默认方法
  2. 静态方法

默认方法

JDK1.8中,接口里面可以定义默认方法。

interface InterfaceName{

​ default returnType methodName(arg-list){ }

}

新时代的程序员:lambda表达式(方法引用)、链式编程、函数式接口、Stream流式计算

interface FirstInterface{
    //传统定义,抽象方法,没有方法体。
    voidbefore();
    //默认方法
    defaultvoidtest() {
        System.out.println("Default method in FirstInterface");
    }
}
classFirstClass implements FirstInterface{
    //所有实现类必须实现接口中未实现的方法。
    @Override
    public void before() {
        System.out.println("我是FirstInterface中的抽象方法,所有实现类必须实现!");
    }
}
public class DefaultMethod {
    publicstaticvoidmain(String[] args) {
        FirstClass fc=newFirstClass();
        fc.test(); //此处输出Default method in FirstInterface,对于默认方法,如果实现类中没有实现就是用默认的。
        fc.before(); //此处输出我是FirstInterface中的抽象方法,所有实现类必须实现。
    }
}

默认方法存在的两大优势

  1. 可以让接口更优雅的升级,减少使用人员操作的负担不必随着接口方法增加,从而修改实现代码,因为默认方法在子类中可以不用实现
  2. 可以让实现类中省略很多不必要方法的空实现

方法调用的判断规则:

  1. 类中声明的方法优先级最高。类或父类中,声明的方法要高于任何默认方法的优先级
  2. 如果无法依据第一条进行判断,那么子接口的优先级更高例如,如果 B 接口继承了 A接口,那么 B 就比 A 更加具体,优先级更高所以,在上述例子中,B是子接口,优先级别更高,调用test方法后输出:defaut method test in B.
  3. 最后,如果还是无法判断,那么继承了多个接口的类,必须通过实现(重写)方法来确定方法的调用

静态方法

JDK1.8中,接口里面可以定义静态方法。

和类中定义的静态方法类似,接口中的静态方法可以直接通过接口名.静态方法名的形式进行访问。

语法:

interface InterfaceName{ 
    static returnType methodName(arg-list){
        //代码实现
    }
}

访问:

InterfaceName.methodName();

注意,接口中的静态方法,只能使用当前接口的名字来调用

Lambda使用

虽然Lambda表达式在java中的实际意义,是对一个接口的实现,但并不是任何接口都可以使用Lambda表达式来进行实现。

原因也很简单,一个Lambda表达式,只是描述了一个函数的参数列表、函数主体、返回类型,那么它顶多是对接口中的一个抽象方法的实现,如果接口中有多个抽象方法呢?

很显然,这时候一个Lambda表达式是无法表示为这个接口的实现,因为无法实现接口中多个抽象方法。

所以,接口中==有且只有==一个抽象方法的时候,才可以使用Lambda表达式来对其进行实现。

方法引用https://www.cnblogs.com/wuhenzhidu/p/10727065.html

函数式接口: 只有一个方法的接口;简化编程模型

@FunctionalInterface 
public interface Runnable {    
    public abstract void run(); 
} 
// JDK1.5:泛型、枚举、反射
// JDK1.8:lambda表达式、链式编程、函数式接口、Stream流式计算 
// 超级多FunctionalInterface 
// 简化编程模型,在新版本的框架底层大量应用!
// foreach(消费者类的函数式接口)

JDK1.8中,针对函数式接口,新增了一个注解@FunctionalInterface,用来检查被标注的接口,是不是一个函数式接口,如果不是,那么编译器会报错。

但是,该注解不是必须要用的,它只是会让编辑器帮我们检查一下而已,以免出现接口中抽象方法的个数不是1的情况。

例如,编译通过

@FunctionalInterface
interface Action{
    int action(int a,int b);
}

例如,编译报错

@FunctionalInterface
interface Action{
    int action(int a,int b);
    int action();
}

Lambda语法

Lambda表达式的格式为:() -> {}

个人理解:Lambda表达式更专注抽象方法种的形参和行为,Lambda传入的参数类型可以省略但是传入的参数只是一个形参,并不是需要传入的实际数据,用于函数主体调用

image-20200310210723763.png
代码测试:

Function

Function函数式接口

给Function<T ,R>接口传入两个参数,返回的参数为传入的第二个参数的类型,而第一个形参作为实参传入apply方法。

QQ截图20200908201803.png
package com.lxy.function;
import java.util.function.Function;
/** 
* Function 函数型接口, 有一个输入参数,有一个输出 
* 只要是 函数型接口 可以 用 lambda表达式简化
*/
public class Demo01 {    
    public static void main(String[] args) {
        //Function<String,String> function = new Function<String,String>() { 
        //            @Override
        //            public String apply(String str) { 
        //                return str; 
        //            } 
        //        };
        Function<String,String> function = (str)->{return str;};
        System.out.println(function.apply("asd"));   
    } 
}
package lambda;

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;

public class TestFunction {
    public static void main(String[] args) {
        String name = "zhangsan";
        // 返回字符串的长度
        int oper = oper(name, str -> str.length());
        oper = oper(name, String::length);

        // 返回字符串的对应下标
        oper = oper(name, str -> (int) str.charAt(0));

        // 遍历数组
        Map<String ,Object> map = new HashMap();
        map.put("张铁林",50);
        map.put("陈一发",20);
        map.put("健字号",52);
        map.put("刘文治",33);
        map.forEach(new BiConsumer<String, Object>() {
            @Override
            public void accept(String s, Object o) {
               // 具体行为
            }
        });
        map.forEach((str,obj) -> System.out.println(str+"->"+obj));

    }
    // 根据给定的字符串的,返回字符串对应具体条件的值.
    public static int oper(String str, Function<String, Integer> function){
        return function.apply(str);
    }
}

Predicate

在使用该接口来做判断的时候,经常需要几个条件同时成立,或者其中一个条件成立,或者求反。

在这种情况下,除了可以在代码中选择使用&&,||,!之外,也可以分别使用这三个方法来代替。

  • and()
  • or()
  • negate()

断定型接口:有一个输入参数,返回值只能是 布尔值!

QQ截图20200908202109.png
package com.lxy.function;
import java.util.function.Predicate;
/**
* 断定型接口:有一个输入参数,返回值只能是 布尔值! 
*/ 
public class Demo02 {   
    public static void main(String[] args) {    
        // 判断字符串是否为空
        //        Predicate<String> predicate = new Predicate<String>(){
        //            @Override
        //            public boolean test(String str) { 
        //                return str.isEmpty(); 
        //            } 
        //        };
        Predicate<String> predicate = (str)->{return str.isEmpty(); };        
        System.out.println(predicate.test(""));
    } 
}
public class PredicateTest {
    public static void main(String[] args) {
        // 普通创建数组方法
        Integer[] arrInteger = new Integer[]{1,20,15,8,9,31,40};
        // jdk1.8创建数组
        // 声明对此方法具体行为的引用,但并不是执行真正的方法
        ActionDemo01 action = (size)->{return new Integer[size];};
        action = Integer[]::new; // 与上面的效果是一样的,主要是上面new Integer[size],下面我们只需要声明就好了
        action.arr(10);


        // 条件1,数据大于10,第一种方式
        List<Integer> filter = filter(arrInteger, new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer > 10;
            }
        });
        // 第二种方式
        filter = filter(arrInteger,(integer)->integer>10);

        // 条件2,数据大于10,并且能被2整除,方式1
        
        // 先写一个大于10断定式接口,之后再写一个能被二整除的断定式接口
        Predicate<Integer> action1 = (integer)->integer>10;
        Predicate<Integer> action2 = (integer)->integer%2==0;
        // 将两个断定式接口组合
        Predicate<Integer> p = action1.and(action2);
        filter = filter(arrInteger,p);

        // 方式2
        filter = filter(arrInteger,(integer -> integer>10 && integer%2==0));

    }
    //定义一个方法,用来过滤数组中所有符合要求的数据。
    public static List<Integer> filter(Integer[] val, Predicate<Integer> con) {
        List<Integer> list = new ArrayList<>();
        for(Integer i:val) {
            if(con.test(i)) {
                list.add(i);
            }
        }
        return list;
    }
}
// 声明接口
interface ActionDemo01{
    Integer[] arr(Integer size);
}

Consumer

Consumer 消费型接口


QQ截图20200908202321.png
package com.lxy.function;
import java.util.function.Consumer;
/**
* Consumer 消费型接口: 只有输入,没有返回值
*/
public class Demo03 {    
    public static void main(String[] args) { 
        //        Consumer<String> consumer = new Consumer<String>() { 
        //            @Override
        //            public void accept(String str) {
        //                System.out.println(str); 
        //            } 
        //        };      
        Consumer<String> consumer = (str)->{System.out.println(str);};        
        consumer.accept("sdadasd");
    } 
}

public class TestConsumer {
    public static void main(String[] args) {
        Data[] datas = new Data[]{new Data("abc"),new Data("123"),new Data("这书评?"),new Data("放学了")};
        // 打印结果,方式1
        oper(datas, new Consumer<Data>() {
            @Override
            public void accept(Data data) {
                System.out.println(data);
            }
        });
        // 打印结果,方式2,因为规定了消费者接口种的泛型为Data所有传入的参数具有Data的行为
        oper(datas,(data -> System.out.println(data.getVal())));
        // 打印结果,方式3,注意返回的是接口!
        Consumer<Data> con = System.out::println;
        oper(datas,con);
        
    }

    // 提供一个方法,用来针对给定的一组Data种的每个值进行操作
    public static void oper(Data[] data, Consumer<Data> consumer){
        for (Data d : data){
            consumer.accept(d);
        }
    }
}

class Data{
    private String val;
    public Data(String val) {
        this.val = val;
    }
    public String getVal() {
        return val;
    }
    public void setVal(String val) {
        this.val = val;
    }
}

Supplier 供给型接口

QQ截图20200908202451.png
package com.lxy.function;
import java.util.function.Supplier;
/**
* Supplier 供给型接口 没有参数,只有返回值
*/ 
public class Demo04 {  
    public static void main(String[] args) {
        //        Supplier supplier = new Supplier<Integer>() { 
        //            @Override 
        //            public Integer get() { 
        //                System.out.println("get()"); 
        //                return 1024; 
        //            }
        //        };
        Supplier supplier = ()->{ return 1024; };       
        System.out.println(supplier.get());    
    }
}
public class TestSupplier {
    public static void main(String[] args) {
        //生成一个1--100以内的随机奇数
        Supplier<Integer> sup = new Supplier<Integer>() {
            @Override
            public Integer get() {
                int result ;
                do {
                    result = (int)(Math.random()*100+1);
                }while((result&1)==0);
                return result;
            }
        };
        int[] vals = consumer(()->{
            int result ;
            do {
                result = (int)(Math.random()*100+1);
            }while((result&1)==0); // 这个写发和result%2==0是一样的只不过效率更高
            return result;
        });
        System.out.println(Arrays.toString(vals));
    }
    //定义一个方法,用来生成10个符合要求的1--100以内的随机数.
    public static int[] consumer(Supplier<Integer> sup) {
        int[] a = new int[10];
        for(int i = 0;i<a.length;i++) {
            a[i] = sup.get();
        }
        return a;
    }
}
上一篇 下一篇

猜你喜欢

热点阅读