java新特性-Lambda表达式

本文深入探讨了Java中的Lambda表达式,它是函数式编程思想的重要体现。通过实例展示了如何使用Lambda替换匿名内部类,简化代码。介绍了Lambda的标准格式、省略模式以及注意事项,并通过三个练习分别展示了无参数、有参数和有返回值的Lambda用法。此外,还对比了Lambda与匿名内部类的区别,强调Lambda在满足特定条件下的高效性和简洁性。
摘要由CSDN通过智能技术生成

Lambda表达式

函数式编程思想

  • 数学中,函数就是有输入量、输出量的一套计算方案,= 拿数据做操作;
  • 面向对象思想:必须通过对象的形式来做事情;
  • 函数式思想:强调做什么,而不是以什么形式去做;
  • Lambda表达式就是函数式思想的体现,也就是强调的是什么而不是以什么形式去做。

案例:
需求:启动一个线程,在控制台输出一句话:多线程程序启动了。
实现方法:

方法1:使用Runnable实现类创建对象
1 创建MyRunnable类实现Runnable接口,重写run方法
public class MyRunnable implements Runnable(){
	@Override
	public void run(){
		System.out.println("多线程启动了!");
	}
}
2 创建MyRunnable类对象
	MyRunnable mr = new MyRunnable();
	
3 创建Thread对象,参数是MyRunnable类对象
	Thread t = new Thread(mr);
	
4 启动线程
	t.start();

方法2:匿名内部类的方式改进(接口的实现类、父类的子类)
new Thread(new Runnable{
	@Override
	public void run(){
		....
	}
}).start();

方法3Lambda表达式的方式改进
new Thread(() -> {
	.....
}).start();
表示的就是:Lambda表达式就是 实现了Runnable接口的实现类。
由于Thread构造方法的参数是Runnable接口,且满足只有一个抽象方法run,所以可以使用Lambda表达式

代码:

// itiheima312.test4
public class Demo1 {
    public static void main(String[] args) {
        * 通过创建Runnable实现类的方式
        // 创建MyRunnable对象(该类是Runnable的实现类)
        /*MyRunnable mr = new MyRunnable();
        // 创建Thread对象,将MyRunnable对象当参数
        Thread t = new Thread(mr);
        // 启动线程
        t.start();*/

        * 匿名内部类的形式
        /*new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多线程程序启动了");
            }
        }).start();*/

        * Lambda表达式的方式改进
        new Thread( () -> {
            System.out.println("多线程程序启动了");
        } ).start();
        // Thread(Runnable target)构造方法,说明传递是Runnable接口的实现类对象,
        // 由于参数是接口,且满足只有一个抽象方法run,所以可以使用run
    }
}

// 实现Runnable接口,重写run方法
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("多线程程序启动了");
    }
}

Lambda表达式的标准格式

匿名内部类格式说明:

new Thread(new Runnable){
	@Override
	public void run(){
	// 方法形式参数为空,说明调用该方法不需要实参
	// 方法返回值是void,说明方法没有返回值
		System.out.println("xxxxxxx");
		// 方法的主体,是我们具体要实现的内容
	}
}).start();

Lambda表达式的标准格式说明:

new Thread(() -> {
	// 理解:() = 方法形式参数;-> = 箭头指向后面要做的事情;{} = 代码块
	System.out.println("xxxx");
}).start();

Lambda表达式的三要素: 形式参数,箭头,代码块
格式:

(形式参数) -> {代码块}
// 形式参数如果有多个,使用逗号隔开即可;没有则空
// -> 表示指向动作
// 代码块:要执行的具体操作

Lambda表达式的使用前提:
有一个接口;接口中有且仅有一个抽象方法

Lambda表达式的三种情况:

  • 抽象方法无参数
方法名/类名( () -> {})
  • 抽象方法有参数
方法名/类名((参数类型 参数名) -> {})
  • 抽象方法有参数有返回值
方法名/类名((参数类型 参数名) -> {return xxx;})
练习1:抽象方法无参数

需求:
定义接口Eatable,里面定义一个抽象方法,void eat()
定义测试类EatableDemo,在测试类中提供两个方法,useEatable(Eatable e)、main()并在其中调用useEatable方法。

// itiheima312.test5
public class EatableDemo {
    public static void main(String[] args) {
        * 使用实现接口的类的形式
/*        EatableImpl e = new EatableImpl();
        useEatable(e);*/

        * 使用匿名内部类的形式
/*        useEatable(new Eatable() {
            @Override
            public void eat() {
                System.out.println("定义人生");
            }
        });*/

        * Lambda表达式
        useEatable(() -> {
            System.out.println("sunshine");
        });
        // 更具上下文环境,useEatable方法的参数是Eatable接口,只有一个抽象方法eat
    }
    private static void useEatable(Eatable e){
        e.eat();
    }
}

// Eatable接口
public interface Eatable {
    void eat();
}

// Eatable接口的实现类
public class EatableImpl implements Eatable {
    @Override
    public void eat() {
        System.out.println("要自己定义自己,别活在比人的评价体系里。");
    }
}
练习2:抽象方法有参数

需求:
定义接口(Flyable),抽象方法:void fly(String s);
定义测试类(FlyableDemo),两个方法:useFlyable(Flyable f)、主方法调用useFlyable

// itiheima312.test6
public class FlyableDemo {
    public static void main(String[] args) {
        // 通过接口实现类的方式实现
        /*FlyableImpl fi = new FlyableImpl();
        useFlyable(fi, "xhj");*/

        // 匿名内部类的形式
        useFlyable(new Flyable() {
            @Override
            public void fly(String s) {
                System.out.println(s + ":梦想靠努力的堆砌");
            }
        },"xhj");

       // Lambda表达式形式
        useFlyable((String s) -> {
            System.out.println(s + ":理想很丰满,现实很骨感");
        },"xhj");
		// useFlyable方法的参数是Flyable接口,有且只有一个方法fly
    }
    private static void useFlyable(Flyable f,String s){
        f.fly(s);
    }
}

// Flyable接口的实现类FlyableImpl
public class FlyableImpl implements Flyable {
    @Override
    public void fly(String s) {
        System.out.println(s + "自述: 行动要追的上梦想");
    }
}

// Flyable接口
public interface Flyable {
    void fly(String s);
}
练习3:抽象方法带参数带返回值

需求:
定义接口Addable,抽象方法int add(int x,int y)
测试类AddableDemo,方法:useAddable(Addable a);主方法,调用useAddable方法

代码:

package itiheima312.test7;
public class AddableDemo {
    public static void main(String[] args) {
        // 使用接口实现类
        /*AddableImpl ai = new AddableImpl();
         useAddable(ai);*/


        // 匿名内部类实现
/*        useAddable(new Addable(){
            @Override
            public int add(int x, int y) {
                return x + y;
            }
        });*/

        // Lambda表达式形式
        useAddable((int x,int y) -> {
            return x+y;
        });
		// useAddable方法的参数是接口
    }
    private static void useAddable(Addable a){
        int sum = a.add(10, 20);
        System.out.println("result:" + sum);
        // 这个方法只是说明useAddable方法 执行a.add方法,并将结果输出,
        // 但是并没有指定add方法的具体执行操作,而Lambda表达式是实现add方法具体实现的。

	// Lambda表达式 x+y 则最后输出是result:30; Lambda表达式 x-y 则最后输出的是result:-10
	// 总结 Lambda表达式 实际上就是接口中抽象方法的具体实现
    }
}

// Addable接口
public interface Addable {
    int add(int x,int y);
}

// Addable接口的实现类AddableImpl
public class AddableImpl implements Addable{
    @Override
    public int add(int x, int y) {
        return x + y;
    }
}

Lambda表达式的省略模式

省略规则

参数类型可以省略
多个参数情况下,不能只省略一个
参数如果只有一个,小括号可以省略
代码块语句只有一条,大括号和分号可以省略
代码块语句只有一条。且有return,需要将大括号、分号、return都省略

代码:

// package itiheima312.test8;
public class LambdaDemo {
    public static void main(String[] args) {
        // 不省略版本
      /*  useAddable((int x,int y) -> {
            return x + y;
        });*/
      1 可以省略参数的类型
/*        useAddable((x,y) -> {
            return x + y;
        });*/
      2 多个参数情况下,不能只省略一个参数的数据类型 此情况会报错。
/*        useAddable((int x,y) ->{
            return x + y;
        });*/
        // 这种情况会报错 原本是int x,int y 现在是int x,y

        // 不省略版本
/*        useFlyable((String s) -> {
            System.out.println(s);
        });*/
        // 省略参数类型
/*        useFlyable((s) -> {
            System.out.println(s);
        });*/
        
        3 若参数有且仅有一个,那么小括号可以省略
       /* useFlyable(s ->{
            System.out.println(s);
        });*/
        
        4 如果代码块的语句只有一条,可以省略大括号和分号.
        useFlyable(s -> System.out.println(s));
        
        5 如果代码块只有一条语句,如有return 则需要省略大括号、分号、return
        useAddable((x,y) -> x + y);
    }
    
    private static void useFlyable(Flyable f){
        f.fly("春回大地,万物复苏");
    }
    private static void useAddable(Addable a){
        int sum = a.add(26, 55);
         System.out.println("小徐和老徐age sum:" + sum);
    }
}
// Addable接口
public interface Addable {
    int add(int x,int y);
}
// flyable接口
public interface Flyable {
    void fly(String s);
}

Lambda表达式的注意事项

  • 使用Lambda必须要接口,并且要求接口中有且仅有一个抽象方法
  • 必须有上下文环境,才能推导出Lambda对应的接口
根据局部变量的赋值得知Lambda对象的接口:类或者方法的参数是接口 且接口只有一个抽象方法
Runnable r = () -> System.out.println("逆风翻盘");
new Thread(Runnable r ).start();

根据调用方法的参数得知Lambda对象的接口:
new Thread(() -> System.out.println("向阳而生")).start();

代码:

package itiheima312.test9;
public class LambdaDemo {
    public static void main(String[] args) {
        // Lambda表达式
  /*      useInter(() -> {
            System.out.println("勤能补拙,所向披靡");
        });*/

        // 使用匿名内部类的形式实现多线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类");
            }
        }).start();
        System.out.println("-----------1");
        
        // 匿名内部类的内容可以通过Lambda表达式替代
        Runnable r = ()-> System.out.println("向阳而生");
        new Thread(r).start();
        System.out.println("-----------2");
        new Thread(() -> System.out.println("逆风翻盘")).start();
    }
    // 输出结果
    /*
    -----------1
    匿名内部类
    -----------2
    向阳而生
    逆风翻盘
    并不是按照书写的顺序,不清楚其中的原因是什么?
    2022/4/21 因为文字的输出都是和线程启动有关系的,线程的启动不是根据书写顺序的,所以不确定那个线程先执行。
*/
    private static void useInter(Inter i){
        i.show();
    }
}

Lambda表达式和匿名内部类区别

区别匿名内部类Lambda表达式
参数不同接口、抽象类、具体类只能是 接口
参数要求不同接口中多于一个抽象方法,
只能使用匿名内部类
接口中有且仅有一个抽象方法,
可以使用Lambda或匿名内部类
编译原理不同编译之后,产生单独的.class字节码文件编译之后,没有单独的.class字节码文件,
对应的字节码文件会在运行的时候动态生成

代码:

package itiheima312.test10;

public class Demo {
    public static void main(String[] args) {
        // 匿名内部类
        /*useInter(new Inter() {
            @Override
            public void show() {
                System.out.println("接口");
            }
        }); // 输出接口 采用匿名内部类形式 参数类型可以是接口
        
        useAnimal(new Animal() {
            @Override
            public void method() {
                System.out.println("抽象类");
            }
        }); // 输出 抽象类 采用匿名内部类形式 参数形式可以是抽象类
        
        useStudent(new Student(){
            @Override
            public void study() {
                System.out.println("具体类");
            }
        }); //输出 具体类  采用匿名内部类形式 参数形式可以是具体类。
        */
        // 说明 匿名内部类 参数形式 可以是 接口、抽象类、具体类。

        System.out.println("--------------");
        
        // Lambda表达式
        useInter(() -> {
            System.out.println("接口");
        });
        // 输出 接口 说明Lambda表达式可以调用方法的形参为接口
        
/*        useAnimal(() -> {
            System.out.println("抽象类");
        });*/
        // 抽象类不可以
        
//        useStudent(() -> System.out.println("具体类"));
        // 具体类不可以

    }
    private static void useStudent(Student s){
        s.study();
    }
    private static void useAnimal(Animal a){
        a.method();
    }
    private static void useInter(Inter i){
        i.show();
    }
}
// 抽象类 Animal
public abstract class Animal  {
    public  abstract void method();
}
// 具体类 Student
public class Student {
    public void study(){
        System.out.println("现在所做的一切,都是为了成为将来更好的自己。");
    }
}
// 接口 Inter
public interface Inter {
    void show();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值