Java8的新特性Lambda

1. Lambda表达式的简介

1.1. Lambda表达式的概念

Lambda表达式,是Java8的一个新特性,也是Java8中最值得学习的新特性之一。
Lambda表达式,从本质来讲,是一个匿名函数。可以使用这个匿名函数,实现接口中的方法。对接口进行非常简洁的实现,从而简化代码。

1.2. Lambda表达式的使用场景

通常来讲,使用Lambda表达式,是为了简化接口实现的。
关于接口的实现,可以有很多方式来实现。例如:设计接口的实现类、使用匿名内部类。但是lambda表达式 ,比这两种方式都简单,

public class Program {

    public static void main(String[] args) {
        // 无参、无返加值的函数式接口
        interfaceImpl();
    }

    public static void interfaceImpl(){
        // 1、使用显式的实现类对象
        SingleReturnSingleParam param1 = new Impl();

        // 2、使用匿名内部类实现
        SingleReturnSingleParam param2 = new SingleReturnSingleParam() {
            @Override
            public int test(int a) {
                return a * a;
            }
        };

        // 3、使用lambda表达式实现
        SingleReturnSingleParam param3 = a -> a * a;

        System.out.println(param1.test(10));
        System.out.println(param2.test(10));
        System.out.println(param3.test(10));
    }

    private static class Impl implements SingleReturnSingleParam{

        @Override
        public int test(int a) {
            return a * a;
        }
    }

    // 定义匿名内部类
    private interface SingleReturnSingleParam{
        int test(int a);
    }
}

1.3. Lambda表达式对接口的要求

虽然说,lambda表达式可以在一定程度上简化接口的实现。但是,并不是所有的接口都可以使用lambda表达式来简化实现的。
lambda表达式毕竟只是一个匿名方法。当实现的接口中的方法过多或者过少的时候,lambda是不适用的。

1.4. 函数式接口

1.4.1. 基础概念

如果说,一个接口中,要求实现类必须实现的抽象方法,有且只有一个!这样的接口,说是函数式接口。

// 这个接口中,有且只有一个方法,是必须实现的,因此是一个函数式接口
interface Test1{
     void test();
 }
 // 这个接口中,实现类必须要实现的方法,有两个,因此不是一个函数式接口
 interface Test2{
     void test1();
     void test2();
 }
 // 这个接口中,实现类必须要实现的方法,有零个!因此不是一个函数式接口
 interface Test3{

 }
 // 这个接口中,虽然没用定义任何的方法,但是可以从父接口中继承到一个抽象方法,是一个函数式接口
 interface Test4 extends Test1{

 }
 // 这个接口中,虽然定义了两个方法,但是defualt方法子类不是必须实现的
 // 因此,实现类实现这个接口的时候,必须实现的方法只有一个!是一个函数式接口
 interface Test5{
     void test5();
     default void test(){};
 }
 // 这个接口中的toString方法,是Object类中定义的方法。
 // 此时,实现类在实现接口的时候,toString可以不重写!因为可以从以从父类Object中继承到!
 // 此时,实现类在实现接口的时候,有且只有一个方法是必须要重写的。是一个函数式接口!
 interface Test6{
     void test6();
     String toString();
 }

思考一下:下面的两个接口是不是函数式接口?

interface Test7{
    String toString();
}
interface Test8{
    void test();
    default void test1(){};
    static void test2(){};
    String toString();

}

1.4.2. @FunctionalInterface

是一个注解,用在接口之前,判断这个接口是否是一个函数式接口。如果是函数式接口,没有任何问题,如果不是一个函数式接口,则会报错。功能类似于@Override。

@FunctionalInterface
interface FunctionalInterfaceTest{
    void test();
}

1.4.3. 系统内置的函数式接口

接口名字参数返回值特殊接口
Predicate< T >TbooleanIntPredicate:参数 int,返回值 boolean
LongPredicate:参数 long,返回值 boolean
DoublePredicate:参数 double,返回值 boolean
Consumer< T >TvoidIntConsumer:参数 int,返回值 void
LongConsumer:参数 long,返回值 void
DoubleConsumer:参数 double,返回值 void
Function<T , R>TRIntFunction< R >:参数 int,返回值 R
IntToDoubleFunction:参数 int,返回值 double
IntToLongFunction:参数 int,返回值 long
LongFunction< R >:参数 long,返回值 R
LongToIntFunction:参数 long,返回值 int
LongToDoubleFunction:参数 long,返回值double
DoubleFunction< R >:参数 double,返回值 R
DoubleToIntFunction:参数 double,返回值 int
DoubleToLongFunction:参数 double,返回值 long
Supplier< T >TBooleanSupplier:参数 无,返回值 boolean
IntSupplier:参数 无,返回值 int
LongSupplier:参数 无,返回值 long
DoubleSupplier:参数 无,返回值 double
UnaryOperator< T >TTIntUnaryOperator :参数 int,返回值 int
LongUnaryOperator :参数 long,返回值 long
DoubleUnaryOperator :参数 double,返回值 int
BinaryOperator< T >T,TTIntBinaryOperator:参数 int,int,返回值 int
LongBinaryOperator:参数 long,long,返回值 long
DoubleBinaryOperator:参数 double,double,返回值 double
BiPredicate<L,R>L,Rboolean
BiConsumer<T,U>T,Uvoid
BIFunction<T,U,R>T,UR

2. Lambda表达式的语法

准备interface!

// 无参无返回值
@FunctionalInterface
public interface NoneReturnNoneParameter {
    void test();
}
// 一个参数无返回值
@FunctionalInterface
public interface NoneReturnSingleParameter {
    void test(int a);
}
// 多个参数无返回值
@FunctionalInterface
public interface NoneReturnMutipleParameter {
    void test(int a,int b);
}
// 无参有返回值 
@FunctionalInterface
public interface SingleReturnNoneParameter {
    int test();
}
// 一个参数有返回值
@FunctionalInterface
public interface SingleReturnSingleParameter {
    int test(int a);
}
// 多个参数有返回值
@FunctionalInterface
public interface SingleReturnMutipleParameter {
    int test(int a,int b);
}

2.1. Lambda表达式的基础语法

lambda表达式,其实本质来讲,就是一个匿名函数。因此在写lambda的时候,不需要关心方法名是什么。
实际上,我们在写lambda表达式的时候,也不需要关心返回值类型。
我们在写lambda表达式的时候,只需要关注两部分内容即可:参数列表方法体

lambda表达式的基础语法:

(参数) -> {
	方法体
};

参数部分: 方法的参数列表,要求和实现的接口中的方法参娄部分一致,包括参数的数量和类型。
方法体的部分: 方法和实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回。
->: 分隔参数部分和方法体部分。

public class BasicSyntax {

    public static void main(String[] args) {
        // 1、实现无参,无返回值的函数式接口
        NoneReturnNoneParameter lambda1 = () -> {
            System.out.println("这是一个无参数无返回值的接口");
        };
        lambda1.test();
        // 2、实现一个参数,无返回值的函数式接口
        NoneReturnSingleParameter lambda2 = (int a) -> {
            System.out.println("这是一个参数,无返回值的方法,参数a = " + a);
        };
        lambda2.test(10);
        // 3、实现多个参数,无返回值的函数式接口
        NoneReturnMutipleParameter lambda3 = (int a,int b) -> {
            System.out.println("这是多个参数,无返回值的方法,参数a = " + a +" 参数b = " + b);
        };
        lambda3.test(100,111);
        // 4、实现无参,有返回值的函数式接口
        SingleReturnNoneParameter lambda4 = () -> {
            System.out.println("这是无参,有返回值的方法,返回值是 = " + 10);
            return 10;
        };
        int test4 = lambda4.test();
        System.out.println(test4);
        // 5、实现一个参数,有返回值的函数式接口
        SingleReturnSingleParameter lambda5 = (int a) -> {
            System.out.println("这是一个参数,有返回值的方法,参数a = " + a);
            return a;
        };
        int test5 = lambda5.test(10);
        System.out.println(test5);
        // 6、实现多个参数,有返回值的函数式接口
        SingleReturnMutipleParameter lambda6 = (int a,int b) -> {
            System.out.println("这是多个参数,有返回值的方法,参数a = " + a +" 参数b = " + b);
            return a + b;
        };
        int test6 = lambda6.test(10, 20);
        System.out.println(test6);
    }
}

2.2. Lambda表达式的进阶语法

在上述代码中,的确可以使用lambda表达式实现接口,但是依然不够简洁,有简化的空间。

2.2.1. 参数部分的简化

  • 参数的类型
    1. 由于在接口的方法中,已经定义了每个参数的类型是什么。而且在使用lambda表达式实现接口的时候,必须要保证参数的数量和类型需要和接口中的方法保持一致。因此,此时lambda表达式中的参数的类型可以省略不写,
    2. 注意事项:
    如果需要省略参数的类型,要保证
    :要省略,每一个参数的类型都必须省略不写。绝对不能出现,有的参数类型省略了,有的参数类型没有省略。
    // 实现多个参数,有返回值的函数式接口
    SingleReturnMutipleParameter lambda = (a,b) -> {
    	System.out.println("这是多个参数,有返回值的方法,参数a = " + a +" 参数b = " + b);
    };
    
  • 参数的小括号
    1. 如果方法的参数列表中的参数数量有且只有一个,此时,参数列表的小括号是可以省略不写的。
    2. 注意事项
    只有当参数的数量是一个的时候,多了,少了都不能省略。
    省略掉小括号的同时,必须要省略掉参数类型。
    如果没有参数的话小括号是不能省略的。
    // 实现一个参数,无返回值的函数式接口
    NoneReturnSingleParameter lambda = a -> {
    	System.out.println("这是一个参数,无返回值的方法,参数a = " + a);
    };
    

2.2.2. 方法体部分的简化

  • 方法体的大括号
    1. 如果方法体中的逻辑,有且只有一句的情况下,那么大括号是可以省略的。

    // 实现一个参数,无返回值的函数式接口
    NoneReturnSingleParameter lambda = a -> System.out.println("这是一个参数,无返回值的方法,参数a = " + a);
    
  • return的精简
    1. 如果一个方法中唯一的一条语句是一个返回语句,此时在省略掉大括号的同时,也省略掉return。

    // 实现一个参数,有返回值的函数式接口
    SingleReturnSingleParameter lambda = a -> a * a;
    

    3. 函数引用

    lambda表达式是为了简化接口的实现的。在lambda表达式中,不应该出现比较复杂的逻辑。如果在lambda表达式中出现了过于复杂的逻辑,会对程序的可读性造成非常大的影响。如果在lambda表达式中需要处理的逻辑比较复杂,一般会单独的写一个方法。在lanbda表达式中直接引用这个方法即可。
    或者
    在有些情况下,我们需要在lambda表达式中实现的逻辑,在另一个地方已经写好了。此时我们就不需要单独写一遍,只需要直接引用这个已经存在的方法即可。

3.1. 静态方法的引用

  • 语法
    • 类::静态方法
  • 注意事项
    • 在引用的方法后面,不要加小括号。
    • 引用的这个方法,参数(数量,类型)和返回值,必须要和接口中定义的一致。
  • 示例
public class Syntax {

    interface Calculate{
        int calulate(int a,int b);
    }

    public static void main(String[] args) {
        // 引用一个非静态方法
        Calculate calculate = new Syntax()::calculate;
        System.out.println(calculate.calulate(50,50));
    }

    private int calculate(int a,int b){
        // 稍微复杂的逻辑
        if (a != b){
            return a - b;
        }
        return a + b;
    }

}

3.2. 非静态方法的引用

  • 语法
    • 对象::非静态方法
  • 注意事项
    • 在引用的方法后面,不要加小括号。
    • 引用的这个方法,参数(数量 ,类型)和返回值,必须要和接口中定义的一致。
  • 示例
public class Syntax {

    interface Calculate{
        int calulate(int a,int b);
    }

    public static void main(String[] args) {
        // 引用一个非静态方法
        Calculate calculate = new Syntax()::calculate;
        System.out.println(calculate.calulate(50,50));
    }

    private int calculate(int x,int y){
        // 稍微复杂的逻辑
        if (x > y){
            return x -y;
        } else if (y > x){
            return y - x;
        }
        return x + y;
    }

}

3.3. 构造方法的引用

  • 使用场景
    • 如果一个函数式接口中定义的方法,仅仅是为了得到一个类的对象。此时我们就可以使用构造方法的引用。简化这个方法的实现。
  • 语法
    • 类名::new
  • 注意事项
    • 可以通过接口的方法的参数,区分引用不同的构造方法。
  • 示例
public class Syntax {

    public static class Person{
        String name;
        int age;

        public Person(){
            System.out.println("Person类无参数的构造方法执行了");
        }
        public Person(String name){
            this.name = name;
            System.out.println("Person类的有参数的构造方法执行了");
        }
        public Person(String name,int age){
            this.name = name;
            this.age = age;
            System.out.println("Person类的多个参数的构造方法执行了");
        }
    }

    @FunctionalInterface
    public static interface GetPersonNoneParameter{
        Person get();
    }

    @FunctionalInterface
    public static interface GetPersonSingleParameter{
        Person get(String name);
    }

    @FunctionalInterface
    public static interface GetPersonMutipleParameter{
        Person get(String name,int age);
    }

    public static void main(String[] args) {
        // 1、使用lambda表达式,实现GetPerson接口
        GetPersonNoneParameter getPerson1 = Person::new;
        // 2、使用lambda表达式,实现一个参数的GetPersonSingleParameter
        GetPersonSingleParameter getPerson2 = Person::new;
        // 3、使用lambda表达式,实现多个参数的GetPersonMutipleParameter
        GetPersonMutipleParameter getPerson3 = Person::new;

        // 因为它们都能够在Person类中找到对应的构造方法
        Person person1 = getPerson1.get();
        Person person2 = getPerson2.get("");
        Person person3 = getPerson3.get("",0);
    }
}

3.4. 对象方法的特殊引用

如果使用lambda表达式,实现某些接口的时候,lambda表达式中包含了某一个对象或者说它的参数中包含了某个对象,此时方法体中,直接使用这个对象调用它的某一个方法就可以完成整体的逻辑,其他的参数,可以作为调用方法的参数。此时,可以对这种实现进行简化。

public class Syntax {
    private static class Person{
        private String name;

        // 提供get和set方法
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void show(){

        }
        public void showTest(int a,int b){

        }
    }

    @FunctionalInterface
    private interface GetNameInterface{
        String get(Person person);
    }

    @FunctionalInterface
    private interface SetNameInterface{
        void set(Person person,String name);
    }

    @FunctionalInterface
    private interface ShowInterface{
        void show(Person person);
    }
    
	@FunctionalInterface
    private interface ShowInterface2{
        void show2(Person person,int a,int b);
    }

    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小明");
        // 这个方法的逻辑就是获取Person的name
        //MyInterface myInterface = p -> p.getName();
        // 通过对象的getName方法直接获取
        GetNameInterface getNameInterface = Person::getName;
        System.out.println(getNameInterface.get(person));

        // 这个方法的逻辑就是为了给对象的某个属性设置参数
        SetNameInterface setNameInterface = Person::setName;
        setNameInterface.set(person,"666");
        System.out.println(getNameInterface.get(person));

        // 和对象中定义的方法参数的返回值一致,直接调用对象里的方法
        ShowInterface showInterface = Person::show;
        showInterface.show(person);
		
		ShowInterface2 showInterface2 = Person::showTest;
		showInterface2.show2(person,100,200);
    }
}

4. Lambda表达式需要注意的问题

这里类似于局部内部类,匿名内部类,依然存在闭包的问题。
如果在lambda表达式中,使用到了局部变量,那么这个局部变量会被隐式的声明为final。是一个常量,不能修改值。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值