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

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;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354