加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java Lambda表达式

发布时间:2020-12-15 05:33:26 所属栏目:Java 来源:网络整理
导读:Java Lambda表达式 概念 函数式接口是指:有且仅有一个抽象方法的接口。 函数式接口即适用于函数式编程场景的接口。而Java中函数式编程体现的就是Lambda,所以Lambda接口就是适用于Lambda使用的接口。有且只有一个抽象方法,Java中的Lambda才能顺利推导。 格

Java Lambda表达式

概念

函数式接口是指:有且仅有一个抽象方法的接口。

函数式接口即适用于函数式编程场景的接口。而Java中函数式编程体现的就是Lambda,所以Lambda接口就是适用于Lambda使用的接口。有且只有一个抽象方法,Java中的Lambda才能顺利推导。

格式

只要确保接口的有且仅有一个抽象方法即可:

修饰符 interface 接口{
    public abstract 返回值类型 方法名称(可选参数信息);
    //其他非抽象方法
}

接口中的public abstract可以省略,所以格式可以简化:

修饰符 interface 接口{
    返回值类型 方法名称(可选参数信息);
    //其他非抽象方法
}

函数式接口可以作为方法的参数和返回值类型

@FunctionalInterface注解
@FunctionalInterface
修饰符 interface 接口{
    返回值类型 方法名称(可选参数信息);
    //其他非抽象方法
}

检测该函数是否是函数式接口,是否有且仅有一个抽象方法。

自定义函数式接口
public class LambdaTest{
    public static void useLambdaInterfaceMethod(LambdaInterface lifm){
        lifm.LambdaInterfaceMethod();
    }
    public static void main(String[] args){
        //传入接口的实现类:
        //useLambdaInterfaceMethod(new 接口的实现类);
        
        //传入接口的匿名内部类
        /*
        useLambdaInterfaceMethod(new LambdaInterface(){
            public void LambdaInterfaceMethod(){
                System.out.println("这里传入的是接口的实现匿名内部类");
            }
        });
        */
        //抽象方法有且仅有一行语句,可以省略大括号
        //ustLambdaInterfaceMethod(() -> System.out.println("..."));
        useLambdaInterfaceMethod(() -> {
            System.out.println("使用Lambda表达式,重写接口中的抽象方法。")
                }
            );
    }
}
//注解FunctionanInterface检测是否为函数式接口
@FunctionalInterface
interface LambdaInterface{
    void LambdaInterfaceMethod();
}

函数式编程

Lambda的延迟执行

有些场景代码执行以后,结果不一定使用,从而造成浪费。而Lambda表达式是延迟执行的,可以作为解决方案,提升性能。

性能浪费的日志案例

日志可以快速定位问题,记录程序运行中的情况,以便项目的监控和优化。

一种场景是对参数进行有条件的使用,例如在日子消息拼接以后,在满足条件时打印输出。

/*
    下面存在性能浪费的问题
    调用log方法的时候,传入第二个参数是一个拼接后的字符串
    是先把字符串拼接好,然后再调用log方法
    log方法中的日志等级不是1级,那么就不会是如此拼接后的字符串
    这里的字符串就白拼接了,造成浪费
*/
public class LoggerTest{
    public static void log(int level,String msg){
        if(level == 1){
            System.out.println(msg);
        }
    }
    public static void main(String[] args){
        String str1 = "hello";
        String str2 = "java";
        log(1,str1 + str2);
    }
}
体验Lambda的更优写法
/*
    使用Lambda优化日志:
        延迟加载
    使用前提:必须存在函数式接口
    使用Lambda表达式作为参数传递,仅仅是把参数传递到log方法中,
    只有满足条件,日志的等级是1级
        才会调用接口LambdaInterface方法中的msgAdd()方法
        然后进行方法的拼接
    如果条件不满足,那么接口LambdaInterface方法中的msgAdd()方法不会执行
        拼接字符串的代码也不会执行,不会存在性能的浪费
*/
public class LoggerTest{
    public static void log(int level,LambdaInterface li){
        if(level == 1){
            String str = li.msgAdd();
            System.out.println(str);
        }
    }
    public static void main(String[] args){
        String str1 = "hello";
        String str2 = "java";
        log(1,() -> {
            //返回一个拼接好的字符串
            return str1 + str2;
        });
    }
}
@FunctionalInterface
interface LambdaInterface{
    //定义一个拼接消息的方法,返回拼接的消息
    String msgAdd();
}
证明Lambda的延迟
public static void main(String[] args){
        String str1 = "hello";
        String str2 = "java";
        log(1,() -> {
            //没有满足条件这里不会执行
            System.out.println("不满足条件");
            //返回一个拼接好的字符串
            return str1 + str2;
        });
    }
使用Lambda作为参数和返回值

Java的Lambda表达式可以被当做匿名内部类的替代品,如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数。
例如:Runnable接口就是一个函数式接口

public class Demo{
    private static void startThread(Runnable runab){
        new Thread(runab).start();
    }
    public static void main(String[] args){
        startThread(() -> System.out.println(...));
    }
}
public  class DemoComparator{
    //返回值类型是一个函数式接口
    //Comparator只有一个抽象方法,所以是函数式接口
    private static Comparator<String> newComparator(){
        return (a,b) -> b.length() - a.length();
    }
    public static void main(String[] args){
        String[] arr = {"a","abs","dfae"};
        //排序
        Arrays.sort(arr,newComparator());
    }
}

如果返回值类型是一个函数式接口,就可以直接返回一个Lambda表达式,如上程序。

注:Comparator有两个抽象方法,int cimpare(T o1,T o2)和boolean equals(Object obj),如果接口声明了一个覆盖java.lang.Object的全局方法之一的抽象方法,那么它不会计入接口的抽象方法数量中,因为接口的任何实现都将具有java.lang.Object或其他地方的实现。

常用的函数式接口

Supplier接口

java.util.function.Supplier 接口包含一个无参的方法:T get();用来获取一个泛型参数指定类型的对象。

import java.util.function.Supplier;
public class SupplierClass{
    public static String getString(Supplier<String> sup){
        return sup.get();
    }
    public static void main(String[] args){
        String str1 = "hello";
        String str2 = "java";
        String getStringMethod = getString(() -> str1 + str2);
        System.out.println(getStringMethod);
    }
}

Supplier 被称为生型接口,指定接口泛型是什么类型,接口中的get方法就返回什么类型。

求数组最大值练习
import java.util.function.Supplier;
public class SupplierClass{
    //返回的是Integer类型
    public static Integer getMax(Supplier<Integer> sup){
        return sup.get();
    }
    public static void main(String[] args){
        int[] iArr = {12,252,-12,435};
        int getMaxInteger = getMax(() -> {
            int max = iArr[0];
            //遍历数组
            for(int i : iArr){
                //如果数组有一个值大于max,则把这个值赋值给max
                if(i > max){
                    max = i;
                }
            }
            //返回数组中的最大值
            return max;
        });
        System.out.println(getMaxInteger);
    }
}
Consumer接口

java.util.function.Consumer 接口消费一个数据,而不是生产一个数据,其数据类型由泛型指定。包含抽象方法accept(T t)

import java.util.function.Consumer;
public class ConsumerClass{
    public static void consumerUseMethod(Consumer<String> com){
        con.accept("被使用的字符串");
    }
    public static void main(String[] args){
        consumerUseMethod((x) -> System.out.println(x));
    }
}
抽象方法:Accept
import java.util.function.Consumer;
public class ConsumerClass{
    public static void consumerUseMethod(String name,Consumer<String> con){
        con.accept(name);
    }
    public static void main(String[] args){
        consumerUseMethod("lilei",(x) -> System.out.println(x));
    }
}
抽象默认方法:andThen

如果一个方法的参数和返回值全是Consumer类型,就可以实现这个效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中default方法antThen:

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

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

import java.util.function.Consumer;
/*
    Consumer接口默认方法andThen
    作用:需要两个Consumer接口,可以把两个Consumer接口组合到一起,对数据进行消费
    
    例如:
    Consumer<String> con1
    Consumer<String> con2
    String s = “hello”;
    con1.accept(s);
    con2.accept(s);
    连接两个Consumer接口,再进行消费
    con1.andThen(con2).accept(s);
*/
public class ConsumerClass{
    public static void consumerUseMethod(String name,Consumer<String> con,Consumer<String> con2){
        //con.accept(name);
        //con2.accept(name);
        //使用andThen方法替代上面代码:
        con.andThen(con2).accept(name);
        //con链接的con2,所以先执行con,再执行con2
    }
    public static void main(String[] args){
        consumerUseMethod(
            "lilei",//第一个Lambda表达式
            (x) -> System.out.println(x),//第二个Lambda表达式
            (x) -> System.out.println("第二个Lambda表达式")
            );
    }
}

如果需要多个Consumer接口,使用andThen方法,就是con1.andThen(con2).andThen(con3)...andThen(conN).accept(t);

Predicate接口

包含一个抽象方法:boolean test(T t),用于条件判断场景
符合条件:返回true
不符合:返回false

import java.util.function.Predicate;

public class PredicateClass{
    public static boolean checkString(String s,Predicate<String> pre){
        //传入要判断的文本
        return pre.test(s);
    }
    public static void main(String[] args){
        String str = "guess how long";
        //判断字符串长度是否大于5,并返回结果
        boolean b = checkString(str,(String s) -> {return str.length() > 5;});
        System.out.println(b);
    }
}

省略一些代码

//省略String s 中类型,可以直接推导
        //只有一行语句,省略大括号和return关键字
        boolean b = checkString(str,s-> str.length() > 5);
默认方法and(与)

将两个Predicate条件使用与逻辑连接起来实现并且的效果,使用默认方法and

default Predicate<T> and(Predicate<? super T> other){
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
}

实现:

import java.util.function.Predicate;

public class PredicateClass{
    public static void checkString(String str,Predicate<String> preOne,Predicate<String> preTwo){
        //preOne.test("str")&&preTwo.test("str");
        boolean b = preOne.and(preTwo).test(str);
        System.out.println(b);
    }
    public static void main(String[] args){
        String strName = "helloworld";
        checkString(strName,s-> s.contains("H"),s-> s.contains("W"));
    }
}
默认方法or(或)
default Predicate<T> or(Predicate<? super T> other){
    Objects.requireNonNull(Other);
    return (t) -> test(t) || other.test(t);
}

实现:

import java.util.function.Predicate;

public class PredicateClass{
    public static boolean checkString(String str,Predicate<String> preTwo){
        //等价于preOne.test(t) || preTwo.test(t);
        return preOne.or(preTwo).test(str);
    }
    public static void main(String[] args){
        String strName = "helloworld";
        boolean b = checkString(strName,s-> s.contains("h"),s-> s.contains("W"));
        System.out.println(b);
    }
}
默认方法negate(非)
default Predicate<T> negate(){
    retuan (t) -> !test(t);
}
//取反
        return preOne.negate(preTwo).test(str);
Function接口

用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。

抽象方法apply

Function最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。

import java.util.function.Function;
public class FunctionClass{
    //Function<原始类型, 要转换的类型>
    public static void applyMethod(String str,Function<String,Integer> fun){
        //字符串类型转换成int类型
        int num = fun.apply(str);
        System.out.println(num + 20);
    }
    public static void main(String[] args){
        String str = "100";
        applyMethod(str,s -> Integer.parseInt(s));
    }
}
默认方法andThen
//用来组合操作
default <V> Function<T,V> andThen(Function<? super R,? extends V> after){
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

实现代码:

import java.util.function.Function;
public class FunctionClass{
    //andThen组合操作
    public static void applyMethod(String str,Integer> funOne,Function<Integer,String> funTwo){
        //字符串类型转换成int类型,加10
        //int num = fun.apply(str) + 10;
        //然后转换成String类型
        //String str = funTwo.apply(num);
        
        //组合,先实现funOne,再实现funTwo
        String strr = funOne.andThen(funTwo).apply(str);
        System.out.println(strr);
    }
    public static void main(String[] args){
        String str = "100";
        //先把字符串转换为integer,结果加10
        //把前面funOne的结果转换成String类型
        applyMethod(str,(String s) -> Integer.parseInt(s) + 10,(Integer in) -> String.valueOf(in));
    }
}

Lambda方法引用

方法引用符号

双冒号::是为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达是函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。

语义分析

例如:System.out对象中有一个重载的println(String)方法恰好就是我们所需要的,那么对于该方法的函数式接口参数,以下两种方式等价:

Lambda表达式: s -> System.out.println(s);
方法引用写法: System.out::println

第一种语义是:拿到参数之后经Lambda手,继而传递给System.out.println方法去处理;
第二种是:直接让System.out中的println方法来取代Lambda。

两种写法的执行效果完全一样。而第二种方法引用复用了已有方案,更加简洁。

Lambda中传递的参数一定是方法引用中那个方法可以接收的类型,否则抛出异常。

推导与省略

如果使用Lambda,那么根据可推导就是可省略的原则,无需指定参数类型,也无需指定重载形式--它们都是自动推导,而如果使用方法引用,也是同样可以根据上下文进行推导。
函数式接口是Lambda的基础,而方法引用是Lambda的孪生兄弟。

通过对象名引用成员方法
/*
    通过对象名引用成员方法
    使用前提:对象名已经存在,成员方法已经存在
*/

public class LambdaObjectUseMethod{
    //定义一个方法
    public static void printString(Printable p){
        p.print("hello");
    }
    public static void main(String[] args){
        //Lambda表达式
        printString(x -> {
            MethodUpper mu = new MethodUpper();
            mu.printUpperCaseString(x);
        });
        
        //方法引用
        //对象必须已经存在
        MethodUpper mu2 = new MethodUpper();
        printString(mu2::printUpperCaseString);
    }
}

//函数式接口
@FunctionalInterface
interface Printable{
    void print(String s);
}



//将字符串全改为大写并输出
class MethodUpper{
    public void printUpperCaseString(String str){
        System.out.println(str.toUpperCase());
    }
}
通过类名称引用静态方法
/*
    通过对象名引用成员方法
    使用前提:类已经存在,静态方法已经存在
*/

public class LambdaObjectUseMethod{
    //定义一个方法
    public static void printString(Printable p){
        p.print("hello");
    }
    public static void main(String[] args){
        //通过类名引用静态方法
        printString(MethodUpper::printUpperCaseString);
    }
}

//函数式接口
@FunctionalInterface
interface Printable{
    void print(String s);
}



//将字符串全改为大写并输出
class MethodUpper{
    //静态方法
    public static void printUpperCaseString(String str){
        System.out.println(str.toUpperCase());
    }
}
通过super引用成员方法
//函数式接口
@FunctionalInterface
interface Greetable{
    void greet();
}

//父类
class FatherClass{
    public void showMethod(){
        System.out.println("我是你爸爸");
    }
}

public class SonClass extends FatherClass{
    public void showMethod(){
        System.out.println("不符solo!!!");
    }
    
    public void useGreetable(Greetable gt){
        gt.greet();
    }
    public void show(){
        /*
        useGreetable(() -> {
            FatherClass fc = new SonClass();
            super.showMethod();
        });
        */
        //super引用成员方法
        useGreetable(super::showMethod);
    }
    
    public static void main(String[] args){
        new SonClass().show();
    }
}
通过this引用成员方法
//函数式接口
@FunctionalInterface
interface Greetable{
    void greet();
}

//父类
class FatherClass{
    public void showMethod(){
        System.out.println("我是你爸爸");
    }
}

public class SonClass extends FatherClass{
    public void showMethod(){
        System.out.println("不符solo!!!");
    }
    
    public void useGreetable(Greetable gt){
        gt.greet();
    }
    public void show(){
        //this引用本类成员方法
        useGreetable(this::showMethod);
    }
    
    public static void main(String[] args){
        new SonClass().show();
    }
}
类构造器的引用

构造器引用使用 类名称::new 的格式

//类构造器引用
class Person{
    String name;
    public Person(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
}

@FunctionalInterface
interface CreatePerson{
    Person returnPerson(String name);
}

public class PersonFunctionClass{
    public static void method(String name,CreatePerson cp){
        Person p = cp.returnPerson(name);
        //Person p = 
        System.out.println(p.getName());
    }
    public static void main(String[] args){
        /*Lambda表达式
        method("lilei",(String name) -> {
            return new Person(name);
        });
        */
        
        /*简化版的Lambda表达式
        method("lilei",name -> new Person(name));
        */
        
        //构造器引用
        method("lilei",Person::new);
    }
}
数组构造器的引用
//数组的构造器引用
@FunctionalInterface
interface ArrayBuiler{
    //创建int数组类型的方法
    int[] createIntArray(int length);
}

//数组的构造器引用
public class ArrayTestClass{
    //定义一个方法,方法参数传递创建数组的长度和ArrayBuiler接口
    //方法内部根据传递的长度创建数组
    public static int[] createArray(int length,ArrayBuiler ab){
        return ab.createIntArray(length);
    }
    
    public static void main(String[] args){
        int[] arr1 = createArray(10,(int i) -> {
            return new int[i];
        });
        
        int[] arr2 = createArray(12,len -> new int[len]);
        
        //引用数组的构造器
        int[] arr3 = createArray(13,int[]::new);
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读