Java中的Lambda表达式
()代表此接口中的唯一方法,"->" 指向一个语句块{},{}语句块中表示这个方法的重写
一、函数式编程思想概述
在数学中,函数就是有输入量,输出量的一套计算方案,也就是“拿数据做操作”。
面向对象思想强调的 “必须通过对象的形式来做事情”
函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”
而这里说的Lambda表达式就是函数式思想的体现
二、体验Lambda表达式
需求:启动一个线程,在控制台输出一句话:多线程程序启动了
步骤:
1、
定义一个类MyRunnable实现Runnable接口,重写run()方法
创建M有Runnable类对象
创建Thread的类对象,把MyRunnable的对象作为构造方法的参数传递
启动线程
2、
使用匿名内部类
3、
使用Lambda表达式
参考代码:
package com.itheima_01;
//多线程接口实现类
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("多线程程序启动了");
}
}
package com.itheima_01;
/*
需求:启动一个线程,在控制台输出一句话:多线程程序启动了
*/
public class LambdaDemo {
public static void main(String[] args){
//一、通过一个实现Runnable接口的实现类,来完成需求
/*MyRunnable my = new MyRunnable();
Thread t = new Thread(my); //通过实现Runnable的自定义类作为构造参数传递
t.start(); //通过start方法启动多线程*/
//可以看到,对这样一个简单需求,使用实现类未免有些麻烦(若是多个线程对象都需要这个实现类下的run那这样就可以)
//使用匿名内存类的方式进行改进
/*new Thread(new Runnable(){
@Override
public void run(){
System.out.println("多线程程序启动了");
}
}).start();*/
//像这样的一个情况下,接口实现中就只有一个方法需要重写
//使用Lambda表达式的方式改进 ()->{}
new Thread(()->{
System.out.println("多线程程序启动了");
}).start();
}
}
三、Lambda表达式的标准格式
1、匿名内部类的写法:
new Thread(new Runnable(){
@Override
public void run(){
System.out.println("多线程程序启动了");
}
}).start();
向匿名对象Thread中的有参构造函数传递了一个匿名内部类
方法的参数为空,无返回值,里面的内容表示我们需要完成的事情
2、Lambda表达式写法(对于其而言,重点关注我们需要做什么,而不是创建对象,这也是函数式编程思想):
可见的是对于匿名内部类而言,Lambda表达式省略了创建子类/实现类的过程,只关注完成哪件事。而对于接口而言接口本身的关注点也是在其中的方法,而并非是成员属性即类。
new Thread(()->{
System.out.println("多线程程序启动了");
}).start();
():表示匿名内部类中重写的函数,参数为空
->:用箭头指向函数的具体实现即{}
{}:包含一段代码块,这里是指的函数的实现体
组成Lambda表达式的三要素:形式参数、箭头、代码块
3、Lambda表达式的格式:
(形式参数)->{代码块}
形式参数:与函数(方法)中的差不多,可以使用函数的写法
->:表示一个指向的动作
代码块:是我们具体要做的事情,也就是函数的实现语句
四、Lambda表达式的练习
Lambda表达式的使用前提:
有一个接口
接口中有且仅有一个抽象方法
练习1:
定义一个接口(Eatable),里面定义一个抽象方法:void eat();
定义一个测试类(EatableDemo),在测试类中提供两个方法
一个方法是:useEatable(Eatable e)
一个方法是主方法,在主方法中调用useEatable方法
参考代码:
package com.itheima_02;
//定义一个接口,其中只有一个抽象方法
public interface Eatable { //创建一个接口interface
public abstract void eat(); //在接口中只有一个抽象方法
}
package com.itheima_02;
//定义一个接口的实现类
public class EatableImpl implements Eatable{
@Override
public void eat(){
System.out.println("吃肉");
}
}
package com.itheima_02;
//主类
public class EatableDemo {
public static void main(String[] args){
//调用方法
//1、传入实现类对象
EatableDemo.useEatable(new EatableImpl()); //使用了匿名对象
//2、传入匿名内部类
EatableDemo.useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("吃水果");
}
});
//3、传入对应的lambda表达式
EatableDemo.useEatable(()->{
System.out.println("吃菜");
});
}
//定义一个方法,使用接口作为参数,需要传入一个对应的实现类,进行多态的实现
public static void useEatable(Eatable eatable){
eatable.eat();
}
}
练习2:
定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s);
定义一个测试类(FlyableDemo),在测试类中提供两个方法
一个方法是:useFlyable(Flyable f)
一个方法是主方法,在主方法中调用useFlyable方法
参考代码:
package com.itheima_03;
//接口
public interface Flyable {
public abstract void fly(String s);
}
package com.itheima_03;
//实现类
public class FlyableImpl implements Flyable{
@Override
public void fly(String s){
System.out.println(s);
}
}
package com.itheima_03;
//主类
public class FlyableDemo {
public static void main(String[] args){
//调用方法
//使用实现类做参数进行传递
FlyableDemo.useFlyable(new FlyableImpl());
//使用匿名内部类作为参数进行传递
FlyableDemo.useFlyable(new Flyable(){
@Override
public void fly(String s){
System.out.println(s);
}
});
//使用lambda表达式作为参数进行传递
FlyableDemo.useFlyable((String s)->{ //此处括号中就有参数了
System.out.println(s);
});
FlyableDemo.useFlyable((s)->{ //还可以这样写(省略了数据类型)
System.out.println(s);
});
}
public static void useFlyable(Flyable flyable){
flyable.fly("测试输出语句");
}
}
练习3:
定义一个接口(Addable),里面定义一个抽象方法:int add(int x, int y);
定义一个测试类(AddableDemo),在测试类中提供两个方法
一个方法是:useAddable(Addable a)
一个方法是主方法,在主方法中调用useAddable方法
参考代码:
package com.itheima_04;
public interface Addable {
public abstract int add(int x, int y);
}
package com.itheima_04;
public class AddableDemo {
public static void main(String[] args){
//调用方法
//使用匿名内部内作为参数传递
AddableDemo.useAddable(new Addable(){
@Override
public int add(int x, int y){
return x + y;
}
});
//使用lambda表达式进行传递
AddableDemo.useAddable((int x, int y)->{ //可以看到无需添加一个返回类型在()前,只需要在实现体中return
return x + y;
});
AddableDemo.useAddable((x, y)->{ //可以看到参数类型可以省略
return x + y;
});
}
public static void useAddable(Addable addable){
int sum = addable.add(10, 30);
System.out.println(sum);
}
}
五、Lambda表达式的省略模式
省略规则:
参数类型可以省略,但是又多个参数的情况下,不能只省略一个
如果参数有且只有一个,那么小括号可以省略
如果代码块语句有且只有一条,可以省略 大阔号 和 分号 ,同时含return时需要省略return
参考代码:
package com.itheima_05;
public interface Addable {
public abstract int add(int x, int y);
}
package com.itheima_05;
public interface Flyable {
public abstract void fly(String s);
}
package com.itheima_05;
public class LambdaDemo {
public static void main(String[] args) {
//常规写法:
/*useAddable((int x, int y)->{
return x + y;
});*/
//1、参数类型可以省略
useAddable((x, y)->{ //且要省略,就所有参数都要省略数据类型
return x + y;
});
//常规写法
/*useFlyable((String s)->{
System.out.println(s);
});*/
/*useFlyable((s)->{
System.out.println(s);
});*/
//2、如果参数“有且只有一个时”,小括号可以省略
/*useFlyable(s->{
System.out.println(s);
});*/
//3、如果代码块的语句只有一条,可以省略大括号和分号
useFlyable(s-> System.out.println(s));
//只有一条语句 且 这一条语句中含 return 还能吧return省略了
useAddable((x, y) -> (x + y));
}
public static void useFlyable(Flyable flyable){
flyable.fly("测试输出语句");
}
public static void useAddable(Addable addable){
int sum = addable.add(10, 20);
System.out.println(sum);
}
}
六、Lambda表达式的注意事项
注意事项:
使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
必须要有上下文环境,才能推导出Lambda对应的接口
例如:将其传递给一个对应接口对象,或者作为参数向接口形参传递(可以参照一下多态)
参考代码:
package com.itheima_06;
public interface Inter {
public abstract void show();
//添加一个抽象方法,发现Lambda表达式便不能使用了
// public abstract void method();
}
package com.itheima_06;
/*
lambda表达式的注意事项
*/
public class LambdaDemo {
public static void main(String[] args){
//方法定义只有一条语句,进行简写
//使用Lambda必须要使用接口,而且接口中有且只有一个抽象方法
useInter(()-> System.out.println("好好学习,天天向上!"));
//必须有上下文环境,此能够推导出Lambda对应的接口
/*new Thread(new Runnable(){
@Override
public void run(){
System.out.println("匿名内部类");
}
}).start(); //对象调用start方法开始启动多线程*/
//只有单个Lambda语句时无法推导出其含义(这样的接口很多,天知道你要干嘛)
// ()-> System.out.println("Lambda表达式");
//指明赋值给对应接口时(或者作为参数传递等),可以推断出
//同时可以看出其本质也是一个实现类对象
Runnable r = ()-> System.out.println("Lambda表达式");
new Thread(r).start();
}
public static void useInter(Inter i){
i.show();
}
}
七、Lambda表达式和匿名内部类的区别
所需类型不同:
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口
使用限制不同:
Lambda表达式只能针对只有一个抽象方法的接口
匿名内部类可以针对有多个抽象方法的接口
实现原理不同:
匿名内部类编译后会生成class字节码文件
Lambda表达式编译后不会生成一个class字节码文件,对应的字节码文件会在运行的时候动态生成
参考代码:
package com.itheima_07;
/*
具体类
*/
public class Student {
public void study(){
System.out.println("爱生活,爱健康");
}
}
package com.itheima_07;
/*
抽象类
*/
public abstract class Animal {
public abstract void method();
}
package com.itheima_07;
/*
接口
*/
public interface Inter {
public abstract void show();
//运行Lambda时注释
public abstract void method();
}
package com.itheima_07;
/*
测试类:
通过测试类、抽象类、接口来体现
*/
public class LambdaDemo {
public static void main(String[] args){
//一、使用匿名内部类
/*//1、此处匿名内部类,相当于这个具体类的子类,并重写了类中的一个方法
useStudent(new Student(){
@Override
public void study(){
System.out.println("具体类");
}
});
//2、此处匿名内部类,相当于这个抽象类的子类,并重写了抽象类中的一个方法
useAnimal(new Animal() {
@Override
public void method() {
System.out.println("抽象类");
}
});
//3、此处匿名内部类,相当于这个接口的实现类,并重写了接口中的一个方法
useInter(new Inter() {
@Override
public void show() {
System.out.println("接口");
}
});*/
//接口中有两个抽象方法时
useInter(new Inter() {
@Override
public void show() {
System.out.println("方法一重写");
}
@Override
public void method() {
System.out.println("方法二重写");
}
});
//二、使用Lambda表达式
//当接口中在添加一个方法后,无法通过编译
// useInter(() -> System.out.println("接口"));
// useAnimal(() -> System.out.println("抽象类")); //无法通过编译
// useStudent(() -> System.out.println("具体类")); //无法通过编译
//三、匿名内部类编译后会生成一个字节码文件,而Lambda表达式不会
//LambdaDemo$1.class (通过模块目录下的out文件运行后比对查看)
}
private static void useStudent(Student s){
s.study();
}
private static void useAnimal(Animal a){
a.method();
}
private static void useInter(Inter i){
//此处值调用了show()方法
i.show();
}
}
标签:25,Java,void,System,println,Lambda,public,out
From: https://www.cnblogs.com/fragmentary/p/16988210.html