Lambda表达式
只要会匿名内部类, 一定会Lambda表达式(简化匿名内部类)
Runnable使用Lambda
public class LamadaTest {
public static void main(String[] args) {
// 强调: 怎么做-------------------------------------
// 面向对象不好的地方:
// 1. 不得不创建一个类, 实现Runnable接口
// 2. 如果不想创建类, 不得不使用匿名内部类
// 3. 使用匿名内部类, 不得不重写run方法
// 4. 重写run方法, 不得不写出跟父类一样的, 返回值类型, 方法名,参数列表
// 5. 最后才要写出run方法里面的代码
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用匿名内部类创建一个线程1");
}
}).start();
// 开启一条新线程 -> 开启线程, 让run方法中的代码执行起来
// 目标: 线程 run里面的代码跑起来
// 我们要做的就剩 指定run方法里面的代码
// 函数式编程: 强调: 做什么
new Thread(() -> System.out.println("开启了一条新线程2")).start();
new Thread(() -> System.out.println("开启了一条新线程3")).start();
new Thread(() -> System.out.println("开启了一条新线程4")).start();
new Thread(() -> System.out.println("开启了一条新线程5")).start();
new Thread(() -> System.out.println("开启了一条新线程6")).start();
new Thread(() -> System.out.println("开启了一条新线程7")).start();
}
}
Lambda表达式的格式和前提
格式
() -> {}
() : 参数列表(一些参数)
-> : 一个箭头 将小括号中的参数, 给大括号中使用
{} : 一段代码
Lambda表达式其实是对匿名内部类的改写
() : 匿名内部类中重写方法的参数列表
{} : 匿名内部类中重写方法的方法体
前提
- 使用的接口, 一定是一个函数式接口(有且只有一个抽象方法的接口)@FunctionalInterface
- 函数式接口要作为方法的参数(主要使用场景)或者返回。
1. 无参无返回的Lambda
package com.free._03lambda.demo02;
@FunctionalInterface
public interface MyInter2 {
void fun();
}
package com.free._03lambda.demo02;
/*
无参无返回的Lambda
模拟: 函数式接口
函数式接口作为参数的方法
关注: 方法的调用
*/
public class Demo02 {
public static void main(String[] args) {
// 方法的调用
print(new MyInter2() {
@Override
public void fun() {
System.out.println("无参无返回的匿名内部类");
}
});
// Lambda
print(() -> System.out.println("无参无返回的Lambda"));
}
// 函数式接口作为参数的方法
public static void print(MyInter2 lambda) { // 传入的一定是实现类对象
lambda.fun();
}
}
2. 无参有返回的Lambda
package com.free._03lambda.demo03;
public interface MyInter3 {
String fun();
}
package com.free._03lambda.demo03;
/*
无参有返回
*/
public class Demo03 {
public static void main(String[] args) {
// 方法的调用
print(new MyInter3() {
@Override
public String fun() {
return "匿名内部类";
}
});
// lambda
print(() -> "Lambda");
}
public static void print(MyInter3 lambda) {
String s = lambda.fun();
System.out.println(s);
}
}
3. 有参无返回的Lambda
package com.free._03lambda.demo04;
public interface MyInter4 {
void fun(int i);
}
package com.free._03lambda.demo04;
/*
有参无返回
*/
public class Demo04 {
public static void main(String[] args) {
print(new MyInter4() {
@Override
public void fun(int i) {
System.out.println("匿名内部类不" + i);
}
});
// lambda
print(i -> System.out.println("Lambda" + i));
}
public static void print(MyInter4 lambda) {
lambda.fun(666);
}
}
4. 有参有返回的Lambda
package com.free._03lambda.demo05;
public interface MyInter5 {
int stringToInt(String s);
}
package com.free._03lambda.demo05;
/*
有参有返回
*/
public class Demo05 {
public static void main(String[] args) {
// 匿名内部类
print(new MyInter5() {
@Override
public int stringToInt(String s) {
// Integer.parseInt(字符串) -> 将数字格式的字符串转换成int类型的数字
return Integer.parseInt(s);
}
});
// lambda
print(s -> Integer.parseInt(s));
// Method Reference
// print(Integer::parseInt);
}
public static void print(MyInter5 lambda) {
int i = lambda.stringToInt("100");
System.out.println(i);
}
}
Comparator使用Lambda
package com.free._03lambda.demo06;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class Demo06 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
// 添加元素
Collections.addAll(list, 5, 4, 2, 6, 8, 7, 9, 3, 1);
// 降叙排序
/*Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});*/
// lambda
Collections.sort(list, (o1, o2) -> o2 - o1);
System.out.println(list);
// 只有引用数据类型的数组, 才能使用比较器排序
Integer[] arr = {2, 4, 3, 5, 1};
/*Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});*/
Arrays.sort(arr, (o1, o2) -> o2 - o1 );
System.out.println(Arrays.toString(arr));
}
}
Lambda的简化格式
原则: 可推导, 可省略.
- 小括号中, 数据类型可以省略
- 小括号中, 如果只有一个参数, 小括号可以省略
- 大括号中, 如果只有一条语句, 可以省略大括号和分号
- 大括号中, 如果只有一条语句且是返回语句,可省略return
alt+enter 自动转换成lamada表达式设置
方法引用
方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。
方法引用的前提
方法引用: 觉得Lambda表达式冗余, 对Lambda继续简化
不是所有的Lambda表达式都可以改成方法引用,
-
Lambda表达式大括号中的内容, 是已经定义好的.
stream.forEach(s -> System.out.println(s));
-
方法引用的格式
:: => 引用运算符 A :: B功能 => 使用A中的B功能
-
方法引用的使用
stream.forEach(s -> System.out.println(s)); // println() 功能已经定义好了 // 使用 System.out 中的 println() 功能 stream.forEach(System.out::println);
方法引用基本使用
方法引用使用一对冒号::
。
下面,我们在 Car 类中定义了 4 个方法作为例子来区分 Java 中 4 种不同方法的引用。
public static class Car {
public static Car create( final Supplier< Car > supplier ) {
return supplier.get();
}
public static void collide( final Car car ) {
System.out.println( "Collided " + car.toString() );
}
public void follow( final Car another ) {
System.out.println( "Following the " + another.toString() );
}
public void repair() {
System.out.println( "Repaired " + this.toString() );
}
}
第一种方法引用的类型是构造器引用,语法是Class::new,或者更一般的形式:Class::new。注意:这个构造器没有参数。
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
第二种方法引用的类型是静态方法引用,语法是Class::static_method。注意:这个方法接受一个Car类型的参数。
cars.forEach( Car::collide );
第三种方法引用的类型是某个类的成员方法的引用,语法是Class::method,注意,这个方法没有定义入参:
cars.forEach( Car::repair );
第四种方法引用的类型是某个实例对象的成员方法的引用,语法是instance::method。注意:这个方法接受一个Car类型的参数:
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
基于静态方法引用的代码演示
public static void main(String args[]) {
List names = new ArrayList();
names.add("大明");
names.add("二明");
names.add("小明");
names.forEach(System.out::println);
}
上面的代码,我们将 System.out::println 方法作为静态方法来引用。
测试结果为:
大明
二明
小明
- 案例一(对象引用成员方法)
public interface Printable {
void print(String s);
}
public class MyRef {
/**
* 定义一个方法, 用来将字符串转换成大写, 然后打印
*/
public void printUpper(String s) {
System.out.println(s.toUpperCase());
}
}
public class Demo01 {
public static void main(String[] args) {
// 匿名内部类
/*printString(new Printable() {
@Override
public void print(String s) {
System.out.println(s);
}
});*/
// 前提条件: 功能一定是已经定义好的
// lambda表达式
// printString(s -> System.out.println(s));
// 方法引用
// printString(System.out::println); // 做打印
// 将字符串转换成大写并打印(这里有println与toUpperCase两个方法,分别定义,不算定义好的)
printString(s -> System.out.println(s.toUpperCase()));
// MyRef 类中 printUpper
// A :: B => MyRef::printUpper
MyRef ref = new MyRef();
printString(ref::printUpper);
}
public static void printString(Printable p) {
p.print("HelloWorld");
}
}
- 案例二(类名引用静态方法)
public interface CalcAble {
int getAbs(int a);
}
public class Demo02 {
public static void main(String[] args) {
// 匿名内部类
/*printAbs(new CalcAble() {
@Override
public int getAbs(int a) {
return Math.abs(a);
}
});*/
// lambda
printAbs(a -> Math.abs(a));
// MethodReference
// 使用了 Math类中的 静态方法 abs功能
// A :: B => 使用A中的B功能
// 方法引用 -> lambda -> 匿名内部类 -> 接口的实现类对象
// fun(Math::abs); // 错误的方式
printAbs(Math::abs);
}
public static void fun(int a) {
System.out.println(a);
}
public static void printAbs(CalcAble c) {
System.out.println(c.getAbs(-10));
}
}
- 案例三(this和super的引用)
public interface GreetAble {
void greet();
}
public class Fu {
public void sayHello() {
System.out.println("大家好, 我是渣渣辉.");
}
}
public class Zi extends Fu {
@Override
public void sayHello() {
System.out.println("大家好, 我是小小春.");
}
public void fun(GreetAble g) {
g.greet();
}
public void method() {
// 匿名内部类 => 生成 Zi$1.class
fun(new GreetAble() {
@Override
public void greet() {
Zi.super.sayHello();
}
});
// lambda没有字节码文件生成
// 打印Fu类的SayHello
fun(() -> super.sayHello());
// 打印Zi类的SayHello
// fun(() -> this.sayHello());
// A中的B功能 super中的sayHello
fun(super::sayHello);
fun(this::sayHello);
}
}
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
- 案例四(类中构造方法的引用)
public interface PersonBuilder {
Person buildPerson(String name, int age);
}
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Demo04 {
public static void main(String[] args) {
printPerson((name, age) -> new Person(name, age));
// 使用了Person中的 new功能
printPerson(Person::new);
}
public static void printPerson(PersonBuilder p) {
System.out.println(p.buildPerson("高圆圆", 18));
}
}
- 案例五(数组创建功能的引用)
public interface ArrayBuilder {
int[] buildArray(int len);
}
public class Demo05 {
public static void main(String[] args) {
printArray(len -> new int[len]);
// 使用数组的new功能
printArray(int[]::new);
}
public static void printArray(ArrayBuilder a) {
int[] arr = a.buildArray(3);
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
System.out.println(Arrays.toString(arr));
}
}