Stream流和Optional | https://blog.csdn.net/qq_45888932/article/details/123018483 |
一、Lambda表达式
java8中推出Lambda表达式。其底层还是通过编译器自动推断并生成匿名类来实现。可以使用更少的代码来实现同样的功能,使代码简洁的同时也使得Java支持闭包的功能
面向函数,忽略面向对象的复杂语法;强调对数据的操作是什么,而不是以什么形式去做
Lambda表达式是函数式编程的体现;可以对匿名内部类的写法进行简化
核心思想:可推导可省略
二、Lambda使用步骤
1.Lambda的语法格式
- 格式:
(形式参数)-> {代码块}
- Lambda表达式的使用前提:
有一个接口,接口中有且仅有一个抽象方法
1.1 lambda和匿名函数的区别
所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口
使用限制不同
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
实现原理不同
匿名内部类:编译之后,产生一个单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
2.无参无返回值
正常写法:
public class demo01 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程启动了");
}
}
使用匿名内部类:
public class demo01 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程又被启动了");
}
});
t1.start();
}
}
使用Lambda表达式:
public class demo01 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("线程又又被启动了");
});
t1.start();
}
}
3.有参数没有返回值
在参数列表的部分传递参数即可
public class demo02 {
public static void main(String[] args) {
// 使用匿名内部类
test(new Test1() {
@Override
public void test1(String st) {
System.out.println("创建实体类:" + st);
}
});
// 使用Lamada
test((String st) -> {
System.out.println("lambda创建:" + st);
});
}
public static void test(Test1 t1) {
t1.test1("测试t1");
}
}
// lamada表达式只能实现具有一个方法的接口,不能是抽象类
interface Test1 {
void test1(String st);
}
4.有参数有返回值
在Lambda表达式中,当代码块中只有一句的情况下,参数列表的括号和return可以省略
public class demo03 {
public static void main(String[] args) {
// 正常lambda
test((int a, int b) -> {return a + b;});
// 缺省参数 参数类型可以省略
test((a, b)-> {return a + b;});
// 缺省参数 当函数体中只有一条语句,可以省略花括号和return
test((a, b) -> a + b);
}
public static void test(Test3 t3) {
int res = t3.sum(2, 3);
System.out.println("返回结果:" + res);
}
}
interface Test3 {
int sum(int a, int b);
}
5.方法引用
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作
如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,就没有必要重复书写代码;这里的意思方法调用处给定了参数,并且方法实现中代码使用了给定的参数,那么在方法的实现时参数就没必要传递了,由编译器自动推断就行(我这么理解的)
通过方法引用来使用已经存在的方案
public class demo04 {
public static void main(String[] args) {
usePrintable(new Printable() {
@Override
public void PrintString(String s) {
System.out.println("我正在修改:" + s + " 新加");
}
});
// 正常lambda
usePrintable((String s) -> {System.out.println("我正在修改:" + s + " 新加使用lambda");});
//缺省参数
usePrintable(s -> {
System.out.println("省略参数类型的lambda " + s);
});
// 这种简化方法不能对原来的字符串进行修改等其他操作;由于传入的参数就是之后使用的,所以就将前后的直接省略了
usePrintable(System.out::println);
}
public static void usePrintable(Printable p) {
p.PrintString("hello java");
}
}
interface Printable {
void PrintString(String s);
}
推导与省略
如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
如果使用方法引用,也是同样可以根据上下文进行推导
方法引用是Lambda的孪生兄弟
5.1 引用类的静态方法
public static int parseInt(String s) 是Integer类中的静态方法
public class demo05 {
public static void main(String[] args) {
test((String s) -> {return Integer.parseInt(s);});
// 简化
test(Integer::parseInt);
}
public static void test(Convert test) {
int res = test.convert("123");
System.out.println("返回结果:" + res);
}
}
interface Convert {
int convert(String str);
}
5.2 引用对象实例方法
即引用类的成员方法,这里调用的 PrintString.printStringCase()
public class demo06 {
public static void main(String[] args) {
// 使用匿名内部类
test(new Printer() {
@Override
public void printUpperCase(String s) {
PrintString ps = new PrintString();
ps.printStringCase(s);
}
});
// 正常lambda
test((String s) -> {
PrintString ps = new PrintString();
ps.printStringCase(s);
});
// 在抽取一层
PrintString ps = new PrintString();
test((s) -> ps.printStringCase(s)); // 此时这个就是方法引用,参数可以省略
// 引用对象的实例方法
test((ps::printStringCase));
}
public static void test(Printer p) {
p.printUpperCase("hello world");
}
}
interface Printer {
void printUpperCase(String s);
}
class PrintString {
public void printStringCase(String s) {
String str = s.toUpperCase();
System.out.println("转换后的字符串:" + str);
}
}
5.3 引用类的实例方法
public interface MyString {
String mySubString(String s,int x,int y);
}
public class MyStringDemo {
public static void main(String[] args) {
//Lambda简化写法
useMyString((s,x,y) -> s.substring(x,y));
//引用类的实例方法
useMyString(String::substring);
}
private static void useMyString(MyString my) {
String s = my.mySubString("HelloWorld", 2, 5);
System.out.println(s);
}
}
5.4 引用构造器
创建一个实体类:
class Student {
private String name;
private int age;
public Student() {
}
public Student(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 "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
对象实例化,匿名函数到lambda的转换
public class demo13 {
public static void main(String[] args) {
test(new StudentFactory() {
@Override
public Student build(String name, int age) {
return new Student(name, age);
}
});
test((name, age) -> {return new Student(name, age);});
test((name, age) -> new Student(name, age));
test(Student::new);
}
public static void test(StudentFactory sf) {
Student student = sf.build("张三", 18);
System.out.println(student);
}
}
interface StudentFactory {
Student build(String name, int age);
}
三、函数式接口
只有一个抽象方法的接口就是函数式接口,可以添加@FunctionalInterface标明,但这个注解是可选项,可以不写。
常用的函数式接口:
根据不用的作用,java8中,内置了4个核心接口
1.Consumer<T> 消费型接口
方法名 | 说明 |
---|---|
void accept(T t) | 对给定的参数执行此操作 |
default Consumer<T> andThen(Consumer after) | 返回一个组合的Consumer,依次执行此操作,然后执行 after操作 |
2.Supplier<T> 供给型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。
方法名 | 说明 |
---|---|
T get() | 按照某种实现逻辑(由Lambda表达式实现)返回一个数据 |
import java.util.function.Supplier;
public class demo08 {
public static void main(String[] args) {
System.out.println(getString(() -> "乔巴"));
}
public static String getString(Supplier<String> sup) {
return sup.get();
}
}
自己实现获取的数据:
public class demo08 {
public static void main(String[] args) {
System.out.println(getString1(str -> str));
}
public static String getString1(TestSupplier ts) {
String res = ts.getString("乔巴");
return res;
}
}
interface TestSupplier {
String getString(String str);
}
3.Function<T,R> 函数型接口, 通常用于参数的处理
方法名 | 说明 |
---|---|
R apply(T t) | 将此函数应用于给定的参数 |
default <V> Function andThen(Function after) | 返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果 |
import java.util.function.Function;
public class demo11 {
public static void main(String[] args) {
convert("123", (str) -> {
return Integer.parseInt(str);
});
convert("123", Integer::parseInt);
}
// 将字符串转换为整数
public static void convert(String str, Function<String, Integer> fun) {
int res = fun.apply(str);
System.out.println(res);
}
}
4.Predicate<T> 断言型接口,或者判断型的接口
方法名 | 说明 |
---|---|
boolean test(T t) | 对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值 |
default Predicate<T> negate() | 返回一个逻辑的否定,对应逻辑非 |
default Predicate<T> and(Predicate other) | 返回一个组合判断,对应短路与 |
default Predicate<T> or(Predicate other) | 返回一个组合判断,对应短路或 |
import java.util.function.Predicate;
public class demo10 {
public static void main(String[] args) {
// 测试一个条件
boolean res = judge("Helloworld", (str) -> str.length() < 11);
System.out.println(res);
// 测试两个条件
res = judge("Helloworld", (str) -> str.length() > 2, (str) -> str.length() < 7);
System.out.println(res);
}
public static boolean judge(String str, Predicate<String> pred1) {
return pred1.test(str);
}
public static boolean judge(String str, Predicate<String> pred1, Predicate<String> pred2) {
return pred1.or(pred2).test(str);
}
}
总结
简单对Lambda表达式的学习进行记录,包括基本Lambda和常用的函数式接口
记录下学习jdk8新特性的lambda表达式和Stream流相关知识
视频资源:b站搜索三更草堂,up小哥很棒的,支持!!