java中的Lambda表达式详解

本文深入探讨Lambda表达式在Java中的应用,从函数式编程思想出发,对比面向对象编程,详解Lambda如何简化代码,提高效率。并通过具体示例,如线程启动、数组排序等,展示Lambda表达式的强大功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Lambda表达式

1. 函数式编程思想和面向对象的思想

在数学中,函数就是有输入量,输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做

  • 面向对象的思想:

    做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情

  • 函数式编程思想:

    只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程

2. 冗余的Runnable代码

2.1 传统的写法

当需要启动一个线程去完成任务时,通常会通过java.lang.Runnable接口来定义任务内容,并使用java.lang.Thread类来启动该线程。代码如下

  • 先写一个Runnable的实现类

    //1. 创建一个Runnable接口的的实现类
    public class Runnable_Impl implements Runnable{
    	@Override
    	//2. 重写run方法,设置线程任务
    	public void run() {
    		System.out.println(Thread.currentThread().getName());
    	}
    }
    
  • 再写一个main

    public class transferRun {
    	public static void main(String[] args) {
    		//3. 新建一个Runnable接口的实现类对象run
    		Runnable_Impl run = new Runnable_Impl();
    		//4. 创建Thread类对象,构造方法中传递Runnable接口的实现类对象run
    		Thread t = new Thread(run);
    		//5. 调用Thread的start方法,启动线程
    		t.start();
    	}
    }
    
2.2 匿名内部类的写法
public class transferRun {
	public static void main(String[] args) {
        //匿名内部类写法1:
		Runnable r = new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}
		};
		new Thread(r).start();
        
        //匿名内部类写法2:
        /*相当于把1写法中的r用代码块new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}
		};   打包传进new Thread(传进这里).start();*/
        new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}		
		}).start();
	}
}
2.3 总结
  • Thread类需要Runnable接口作为参数,其中重写run方法是核心,这时候,不得不创建一个Runnable接口的实现类Runnable_Impl
  • 就算用了匿名内部类的方法,省去了写一个Runnable实现类的功夫,但是还是要重写run方法,还是要把run方法名称、方法参数、方法返回值写一遍
  • 其实全部代码,只有System.out.println(Thread.currentThread().getName());这句不是废话。因为我们到头来都是为了这句代码能运行起来。

3. 编程思想转变

3.1 关注做什么,而不是怎么做

像上面代码中,我们真正向做的是把run方法内部的代码运行起来,但是受限于面向对象的规则约束,不得不再创建一个Runnable的实现类等等,我们实际上不想做这些功夫。而Lambda表达式的出现帮助我们解决了代码冗余的问题。

3.2 体验Lambda的更优写法(简化匿名内部类中对接口的书写)

2014年3月Oracle发布的java8 (JDK 1.8)中,加入了Lambda表达式这一重量级新特性

public class tryLambda {
	public static void main(String[] args) {
		//对照匿名内部类的写法
		new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}		
		}).start();
		
        
		//Lambda表达式的写法,显然Lambda写法更简单
		new Thread(()-> {
			System.out.println(Thread.currentThread().getName());
		}).start();
	}
}

4. Lambda语法

Lambda表达式的标准格式:
() -> {System.out.println("powerful Lambda");}
  • 由三部分组成:

    • 一些参数
    • 一个箭头
    • 一段代码
  • 格式:

    (参数列表)-> {一些重写方法的代码};

  • 解释说明格式:

    • () : 接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔
    • -> : 传递的意思,把参数传递给方法
    • {} : 重写接口的抽象方法的方法体

5. 练习:使用Lambda标准格式(无参数无返回值)

题目

给定一个厨子Cook接口,内含唯一的抽象方法makeFood,且无参数、无返回值。如下:

public interface Cook{
    void makeFood();
}

在下面的代码中,请使用Lambda的标准格式调用invokeCook方法,打印输出“吃饭啦!“字样

public class Demo01InvokeCook{
    public static void main(String[] args){
        //请在此使用Lambda【标准格式】调用InvokeCook方法
    }
    
    public static void invoeCook(Cook cook){
        cook.maeFood();
    }
}
答案
public class Demo01InvokeCook{
    public static void main(String[] args){
        //首先,我们看看用匿名内部类的写法是怎样的
    	//调用invokeCook方法,参数是Cook接口,传递Cook接口的匿名内部类对象
    	invokeCook(new Cook() {
			@Override
			public void makeFood() {
				System.out.println("吃饭了");
			}		
    	});
    	
    	//用Lambda表达式的写法,简化匿名内部类的书写
    	//依然是先写invokeCook();,然后在这括号里面重写方法
    	//由于Cook接口里面的makeFood () 方法是没有参数的,所以直接写(),然后写箭头->
    	//最后写方法体{System.out.println("吃饭了");}
    	invokeCook(()->{System.out.println("吃饭了");});
    }
    
    public static void invokeCook(Cook cook){
        cook.makeFood();
    }
}

6. Lambda有参数有返回值的写法

需求:
使用数组存储多个person对象
数组中的person对象使用Arrays的sort方法通过年龄进行升序排序
Arrays的sort方法有两个参数,一个是数组,一个是Comparator接口
分析:
  • 创建一个person类

    public class person {
    	//名字
    	private String name;
    	//年龄
    	private int age;	
    	
    	//无参构造函数
    	public person() {
    	}
    	
    	//有参构造函数
    	public person(String name, int age) {
    		this.name = name;
    		this.age = age;
    	}
    
    	//重写tostring方法
    	@Override
    	public String toString() {
    		return "person [name=" + name + ", age=" + age + "]";
    	}
    	
    	//获取名字
    	public String getName() {
    		return name;
    	}
    	
    	//获取年龄
    	public int getAge() {
    		return age;
    	}
    }
    
  • 传统写法:用匿名内部类方式

    import java.util.Arrays;
    import java.util.Comparator;
    
    /*Lambda表达式有参数返回值的练习
     * 需求:
     * 		使用数组存储多个person对象
     * 		对数组中的person对象使用Arrays的sort方法通过年龄进行升序排序
     */
    public class Demo02person {
    	public static void main(String[] args) {
    		//使用数组存储多个person对象
    		person[] beautys = {
    				new person("柳岩", 21),
    				new person("迪丽热巴", 19),
    				new person("杨幂", 18)
    		};
    		
    		//对数组中的person对象使用Arrays的sort方法通过年龄进行升序(前边 - 后边)排序
    		//Comparator是一个接口,我们可以用匿名内部类的方式重写它里面的compare方法
    		Arrays.sort(beautys, new Comparator<person>() {
    			@Override
    			public int compare(person o1, person o2) {
    				//升序(前边 - 后边)
    				return o1.getAge() - o2.getAge();
    			}
    		});
    		
    		//打印结果
    		for (person person : beautys) {
    			System.out.println(person.toString());
    		}
    	}
    }
    /*
     person [name=杨幂, age=18]
     person [name=迪丽热巴, age=19]
     person [name=柳岩, age=21]
     */
    
  • Lambda写法

    import java.util.Arrays;
    import java.util.Comparator;
    
    /*Lambda表达式有参数返回值的练习
     * 需求:
     * 		使用数组存储多个person对象
     * 		对数组中的person对象使用Arrays的sort方法通过年龄进行升序排序
     */
    public class Demo02person {
    
    	public static void main(String[] args) {
    		//使用数组存储多个person对象
    		person[] beautys = {
    				new person("柳岩", 21),
    				new person("迪丽热巴", 19),
    				new person("杨幂", 18)
    		};
            
    	    //由于Comparator是一个接口,不用再new什么了,直接开始写Lambda三要素
            //第一个要素 () : 这次compare方法有两个参数person o1和person o2
            //第二个要素 -> : 直接写
            //第三个要素 {} :写方法体,return o1.getAge() - o2.getAge();
    		Arrays.sort(beautys, (person o1, person o2) -> {return o1.getAge() - o2.getAge();});
    		
    		//打印结果
    		for (person person : beautys) {
    			System.out.println(person.toString());
    		}
    	}
    }
    

7. 练习:使用Lambda(有参数有返回值)

题目

给定一个计算器Calculator接口,内含抽象方法calc可以将两个int数字相加得到和值

public interface Calculator{
    int calc(int a, int b);
}

在下面的代码中,请使用Lambda的标准格式调用invokeCalc方法,完成120和130相加

public class Demo03InvokeCalc{
    public static void main(String[] args){
        //请在此使用Lambda【标准格式】调用invokeCalc方法来计算120+130的结果

    }
    
    public static void invokeCalc(int a, int b, Calculator calculator){
        int result = calculator.calc(a, b);
        System.out.println("结果是:" + result);
    }
}
答案
public class Demo03InvokeCalc{
    public static void main(String[] args){
        //请在此使用Lambda【标准格式】调用invokeCalc方法来计算120+130的结果
    	int a = 120, b = 130;
    	invokeCalc(a, b, (int c, int d) -> {return c + d;});
    	
    	/*匿名内部类的写法
    	invokeCalc(a, b, new Calculator() {
			@Override
			public int calc(int a, int b) {
				return a + b;
			}
		}); */
    }
    
    public static void invokeCalc(int a, int b, Calculator calculator){
        int result = calculator.calc(a, b);
        System.out.println("结果是:" + result);
    }
}

8. Lambda的省略写法

可推导可省略

Lambda强调的是“做什么”而不是“怎么做”,所以凡是可以根据上下文推导得知的信息,都可以省略。例如上例还可以使用Lambda的省略写法

刚开始的写法

	int a = 120, b = 130;
    invokeCalc(a, b, (int c, int d) -> {return c + d;});

省略的写法

	int a = 120, b = 130;
    invokeCalc(a, b, (c, d) -> c + d);

区别

修饰c和d的类型可以省略,因为在方法在接口中声明时,已经知道c和d的类型
省略了大括号{},因为只有一句代码return c + d;,不用大括号
省略了return,因为方法在接口中声明时,就明确了有int返回,省略了return,就要把分号也省掉

9. Lambda使用前提

  • lambada的语法非常简洁,完全没有面向对象的束缚。但是使用时有几个问题需要特别注意:

    • 使用Lambda必须具有接口,且要求接口中仅有一个抽象方法,无论是JDK内置的Runnable、Comparator接口还是自定义接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda

    • 使用Lambda必须具有上下文判断

      也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值