一、Lambda表达式
1.概述:
从JDK1.8开始为了简化使用者进行代码开发,专门提供有Lambda表达式的支持,利用此操作形式可以实现函数式的编程,对于函数式编程比较著名的语言:Scala,利用函数式的编程可以避免掉面向对象编程之中的一些繁琐的问题。 面向对象在其长期发展的过程中一直有一部分的反对者认为面向对象过于繁琐。
2.使用场景:
3.Lambda表达式的演化
案例:员工过滤年和工资
需求1:过滤出年龄是大于18岁的员工集合
需求2:过滤出薪资是大于2000的员工集合
初始版本(演化一):
import java.util.ArrayList;
import java.util.List;
class Staff {
private String id;
private String name;
private int age;
private int salary; //薪水
public Staff() {
}
public Staff(String id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Staff{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
public class LambdaDemo1 {
public static void main(String[] args) {
/*
需求1:过滤出年龄是大于18岁的员工集合
需求2:过滤出薪资是大于2000的员工集合
*/
//创建一个员工
ArrayList<Staff> staffList = new ArrayList<>();
staffList.add(new Staff("1001", "小明", 22, 10000));
staffList.add(new Staff("1002", "小花", 17, 3000));
staffList.add(new Staff("1003", "小帅", 24, 20000));
staffList.add(new Staff("1004", "小李", 19, 1000));
staffList.add(new Staff("1005", "小王", 16, 500));
System.out.println("==================需求1:过滤出年龄是大于18岁的员工集合=======================");
List<Staff> staffRes1 = filterStaffWithAge(staffList);
for (Staff staff : staffRes1) {
System.out.println(staff);
}
System.out.println("==================需求2:过滤出薪资是大于2000的员工集合=======================");
List<Staff> staffRes2 = filterStaffWithSalary(staffList);
for (Staff staff : staffRes2) {
System.out.println(staff);
}
}
public static List<Staff> filterStaffWithAge(ArrayList<Staff> staffList) {
ArrayList<Staff> staffArrayList = new ArrayList<>();
for (Staff staff : staffList) {
if (staff.getAge() > 18) {
staffArrayList.add(staff);
}
}
return staffArrayList;
}
public static List<Staff> filterStaffWithSalary(ArrayList<Staff> staffList) {
ArrayList<Staff> staffArrayList = new ArrayList<>();
for (Staff staff : staffList) {
if (staff.getSalary() > 2000) {
staffArrayList.add(staff);
}
}
return staffArrayList;
}
}
演化二:
两个方法的逻辑是一样的,减少方法,将不同需求写在接口中,把接口的具体实现子类当做参数传入方法,在一个方法中完成改写。
import java.util.ArrayList;
import java.util.List;
interface FilterStaff {
boolean filterStaffWithAny(Staff staff);
}
class FilterStaffWithAge implements FilterStaff {
@Override
public boolean filterStaffWithAny(Staff staff) {
return staff.getAge() > 18;
}
}
class FilterStaffWithSalary implements FilterStaff {
@Override
public boolean filterStaffWithAny(Staff staff) {
return staff.getSalary() > 2000;
}
}
public class lambdaDemo2 {
public static void main(String[] args) {
//创建一个员工
ArrayList<Staff> staffList = new ArrayList<>();
staffList.add(new Staff("1001", "小明", 22, 10000));
staffList.add(new Staff("1002", "小花", 17, 3000));
staffList.add(new Staff("1003", "小帅", 24, 20000));
staffList.add(new Staff("1004", "小李", 19, 1000));
staffList.add(new Staff("1005", "小王", 16, 500));
System.out.println("==================需求1:过滤出年龄是大于18岁的员工集合=======================");
List<Staff> staffRes1 = filterStaff(staffList, new FilterStaffWithAge());
for (Staff staff : staffRes1) {
System.out.println(staff);
}
System.out.println("==================需求2:过滤出薪资是大于2000的员工集合=======================");
List<Staff> staffRes2 = filterStaff(staffList, new FilterStaffWithSalary());
for (Staff staff : staffRes2) {
System.out.println(staff);
}
}
public static List<Staff> filterStaff(ArrayList<Staff> staffList, FilterStaff filterStaff) {
ArrayList<Staff> staffArrayList = new ArrayList<>();
for (Staff staff : staffList) {
if (filterStaff.filterStaffWithAny(staff)) {
staffArrayList.add(staff);
}
}
return staffArrayList;
}
}
演化三:
因为是接口的实现,所以可以用匿名内部类改写
import java.util.ArrayList;
import java.util.List;
public class lambdaDemo3 {
public static void main(String[] args) {
//创建一个员工
ArrayList<Staff> staffList = new ArrayList<>();
staffList.add(new Staff("1001", "小明", 22, 10000));
staffList.add(new Staff("1002", "小花", 17, 3000));
staffList.add(new Staff("1003", "小帅", 24, 20000));
staffList.add(new Staff("1004", "小李", 19, 1000));
staffList.add(new Staff("1005", "小王", 16, 500));
System.out.println("==================需求1:过滤出年龄是大于18岁的员工集合=======================");
//使用匿名内部类的方式传参
List<Staff> staffRes1 = filterStaff(staffList, new FilterStaff() {
@Override
public boolean filterStaffWithAny(Staff staff) {
return staff.getAge() > 18;
}
});
for (Staff staff : staffRes1) {
System.out.println(staff);
}
System.out.println("==================需求2:过滤出薪资是大于2000的员工集合=======================");
List<Staff> staffRes2 = filterStaff(staffList, new FilterStaff() {
@Override
public boolean filterStaffWithAny(Staff staff) {
return staff.getSalary() > 2000;
}
});
for (Staff staff : staffRes2) {
System.out.println(staff);
}
}
public static List<Staff> filterStaff(ArrayList<Staff> staffList, FilterStaff filterStaff) {
ArrayList<Staff> staffArrayList = new ArrayList<>();
for (Staff staff : staffList) {
if (filterStaff.filterStaffWithAny(staff)) {
staffArrayList.add(staff);
}
}
return staffArrayList;
}
}
最终版本(演化四):
使用lambda表达式改写
import java.util.ArrayList;
import java.util.List;
public class lambdaDemo4 {
public static void main(String[] args) {
//创建一个员工
ArrayList<Staff> staffList = new ArrayList<>();
staffList.add(new Staff("1001", "小明", 22, 10000));
staffList.add(new Staff("1002", "小花", 17, 3000));
staffList.add(new Staff("1003", "小帅", 24, 20000));
staffList.add(new Staff("1004", "小李", 19, 1000));
staffList.add(new Staff("1005", "小王", 16, 500));
System.out.println("==================需求1:过滤出年龄是大于18岁的员工集合=======================");
//使用匿名内部类的方式传参
// List<Staff> staffRes1 = filterStaff(staffList, new FilterStaff() {
// @Override
// public boolean filterStaffWithAny(Staff staff) {
// return staff.getAge() > 18;
// }
// });
/**
* e -> e.getAge() > 18
*
* e:表示的就是一个Staff员工对象
* e.getAge() > 18 : 表示的是接口的实现逻辑
*
*/
List<Staff> staffRes1 = filterStaff(staffList, e -> e.getAge() > 18);
for (Staff staff : staffRes1) {
System.out.println(staff);
}
System.out.println("==================需求2:过滤出薪资是大于2000的员工集合=======================");
// List<Staff> staffRes2 = filterStaff(staffList, new FilterStaff() {
// @Override
// public boolean filterStaffWithAny(Staff staff) {
// return staff.getSalary() > 2000;
// }
// });
List<Staff> staffRes2 = filterStaff(staffList, e -> e.getSalary() > 2000);
for (Staff staff : staffRes2) {
System.out.println(staff);
}
}
public static List<Staff> filterStaff(ArrayList<Staff> staffList, FilterStaff filterStaff) {
ArrayList<Staff> staffArrayList = new ArrayList<>();
for (Staff staff : staffList) {
if (filterStaff.filterStaffWithAny(staff)) {
staffArrayList.add(staff);
}
}
return staffArrayList;
}
}
4.Lambda表达式基本语法
5. 分类
(1)无参数,无返回值
/*
1. 无参数,无返回值
*/
interface Inter {
void fun1();
}
public class lambdaDemo5 {
public static void main(String[] args) {
show(() -> System.out.println("hello world"));
}
public static void show(Inter inter) {
inter.fun1();
}
}
(2) 有一个参数,无返回值
/*
有一个参数,无返回值
*/
interface Inter1 {
void fun1(String s);
}
public class lambdaDemo6 {
public static void main(String[] args) {
show("加油", e -> System.out.println("好好学习,天天向上" + e));
}
public static void show(String s, Inter1 inter) {
inter.fun1(s);
}
}
6.Lambda表达式注意事项
(1)当接口的参数个数只有一个的适合,改写Lambda表达式的时候,左边小括号可以省略
(2)当接口的参数大于等于2个的时候,改写Lambda表达式的时候,左边小括号不可以省略
(3)当接口的实现逻辑大于等于2行的话,改写Lambda表达式的时候,右边大括号不能省略
(4)当接口的方法有返回值类型的时候,写Lambda表达式的时候,右边要有return
演示:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/*
若只有一个参数,小括号可以省略不写
*/
@FunctionalInterface //这个注解是可选的,它可以确保接口仅包含一个抽象方法
interface Inter5 {
void fun1(int n);
}
/*
有两个以上的参数, 无返回值的
*/
interface Inter6 {
void fun1(int a, int b);
}
/*
有若干个参数,并且方法有返回值
*/
interface Inter7 {
List<String> fun1(String s1, String s2);
}
/*
有若干个参数,并且方法有返回值
*/
interface Inter8 {
String fun1(String s1, String s2);
}
public class LambdaDemo7 {
public static void main(String[] args) {
//需求:接口中的逻辑是直接输出n的值
// show1(10, (e) -> System.out.println(e));
//TODO:结论1:当接口的参数个数只有一个的适合,改写Lambda表达式的时候,左边小括号可以省略
show1(10, e -> System.out.println(e));
//需求:接口中的逻辑求两个数相乘的输出
//TODO:结论2:当接口的参数大于等于2个的时候,改写Lambda表达式的时候,左边小括号不可以省略
show2(3, 4, (e1, e2) -> System.out.println(e1 * e2));
//需求:调用show1方法,要求对传入的数值进行字符串拼接,转字节数组,将字节数组以字符串的形式输出
//TODO:结论3:当接口的实现逻辑大于等于2行的话,改写Lambda表达式的时候,右边大括号不能省略
show1(10, e -> {
String s = e + ":数加";
byte[] bytes = s.getBytes();
System.out.println(Arrays.toString(bytes));
});
//需求:将两个字符串封装到一个集合中,返回
//TODO:结论4:当接口的方法有返回值类型的时候,写Lambda表达式的时候,右边要有return
List<String> list1 = show3("hello", "world", (a, b) -> {
ArrayList<String> list = new ArrayList<>();
list.add(a);
list.add(a);
return list;
});
System.out.println(list1);
//需求:将两个字符串进行拼接操作,返回
//TODO:结论5:当接口的方法有返回值类型的时候,并且改写写Lambda表达式的时候,右边语句体只有一行语句作为返回值,那么可以省略return
String s = show4("hello", "world", (a, b) -> a + b);
System.out.println(s);
}
public static String show4(String s1, String s2, Inter8 inter8) {
return inter8.fun1(s1, s2);
}
public static List<String> show3(String s1, String s2, Inter7 inter7) {
return inter7.fun1(s1, s2);
}
public static void show1(int i, Inter5 inter5) {
inter5.fun1(i);
}
public static void show2(int a, int b, Inter6 inter6) {
inter6.fun1(a, b);
}
}
7.函数式接口
(1)概述:
(2)分类
Predicate<T> 断言型接口
演示:
package com.shujia.day19.jiekou;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class Demo1 {
public static void main(String[] args) {
//创建一个员工
ArrayList<Staff> staffList = new ArrayList<>();
staffList.add(new Staff("1001", "小明", 22, 10000));
staffList.add(new Staff("1002", "小花", 17, 3000));
staffList.add(new Staff("1003", "小帅", 24, 20000));
staffList.add(new Staff("1004", "小李", 19, 1000));
staffList.add(new Staff("1005", "小王", 16, 500));
System.out.println("==================需求1:过滤出年龄是大于18岁的员工集合=======================");
List<Staff> staffRes1 = filterStaff(staffList, e -> e.getAge() > 18);
for (Staff staff : staffRes1) {
System.out.println(staff);
}
System.out.println("==================需求2:过滤出薪资是大于2000的员工集合=======================");
List<Staff> staffRes2 = filterStaff(staffList, e -> e.getSalary() > 2000);
for (Staff staff : staffRes2) {
System.out.println(staff);
}
}
public static List<Staff> filterStaff(ArrayList<Staff> staffList, Predicate<Staff> predicate) {
ArrayList<Staff> staffArrayList = new ArrayList<>();
for (Staff staff : staffList) {
if (predicate.test(staff)) {
staffArrayList.add(staff);
}
}
return staffArrayList;
}
}
Function<T,R> 函数型接口
演示:
import java.util.function.Function;
/*
Function<T,R> 函数型接口
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
*/
public class Demo2 {
public static void main(String[] args) {
String s = show("你好", e -> "我说:" + e);
System.out.println(s);
}
public static String show(String s, Function<String, String> function) {
return function.apply(s);
}
}
Supplier<T> 供给型接口
演示:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
/*
供给型接口
@FunctionalInterface
public interface Supplier<T> {
T get();
}
随机生成10个1-100的数据到集合返回
*/
public class Demo3 {
public static void main(String[] args) {
List<Integer> integerList = getNumber(() -> (int) (Math.random() * 100 + 1));
for (Integer i : integerList) {
System.out.println(i);
}
}
public static List<Integer> getNumber(Supplier<Integer> supplier) {
ArrayList<Integer> arrayList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
Integer n = supplier.get();
arrayList.add(n);
}
return arrayList;
}
}
Consumer<T> 消费型接口
演示:
import java.util.function.Consumer;
/*
消费型接口
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
*/
public class Demo4 {
public static void main(String[] args) {
show(10,e-> System.out.println(e));
}
public static void show(int i, Consumer<Integer> consumer) {
consumer.accept(i);
}
}
8.Lambda用法再简洁之方法引用
(1)对象的引用 :: 实例方法名
(2)类 :: 静态方法名
(3)类 :: 实例方法名
(4)构造方法引用 ClassName::new
(5)数组引用
(1)~(5)演示:
import java.util.Arrays;
import java.util.function.BiConsumer;
import java.util.function.Function;
class Student {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void show(String s1, String s2) {
System.out.println(s1 + "--" + s2);
}
public void show2(String s1, String s2) {
System.out.println(s1 + "##" + s2);
}
public static void show3(String s1, String s2) {
System.out.println(s1 + "@@" + s2);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
class JiaHua {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.getName());
// fun("张三", "18", (a, b) -> student.show(a, b));
//TODO: 改写1:当lambda表达式中右边的对象.方法(参数),其中的参数与左边的参数一致的时候,可以进行改写成 对象名::方法名
fun("张三", "18", student::show2);
//TODO: 改写2:当lambda表达式中右边的类名.静态方法(参数),其中的参数与左边的参数一致的时候,可以进行改写成 类名::静态方法名
// fun("李四", "19", (a, b) -> Student.show3(a, b));
fun("李四", "19", Student::show3);
//TODO: 改写3:当lambda表达式中右边的变量1.方法(变量2,[变量3,..]),右边的逻辑是将左边从第二个变量开始,都作为第一个变量的方法参数的时候,可以进行改写成 类名::方法名
// fun2(student, "尚平", (s, n) -> s.setName(n));
fun2(student, "尚平", Student::setName);
System.out.println(student.getName());
//TODO: 改写4:当lambda表达式中右边的new 类名(变量1,..), 其中变量是左边的变量 可以进行改写成 类名::new
// Student student1 = fun3("小虎", e -> new Student(e));
Student student1 = fun3("小虎", Student::new);
System.out.println(student1);
//TODO: 改写5:当lambda表达式中右边的new 数据类型[变量], 其中变量是左边的变量 可以进行改写成 数据类型[]::new
// int[] ints = fun4(5, e -> new int[e]);
int[] ints = fun4(5, int[]::new);
System.out.println(Arrays.toString(ints));
}
public static int[] fun4(int number, Function<Integer, int[]> function) {
return function.apply(number);
}
public static Student fun3(String name, Function<String, Student> function) {
return function.apply(name);
}
public static void fun2(Student student, String name, BiConsumer<Student, String> biConsumer) {
biConsumer.accept(student, name);
}
public static void fun(String a, String b, BiConsumer<String, String> biConsumer) {
biConsumer.accept(a, b);
}
}
(6)注意:
二.枚举
1.概述:
枚举类型是Java中一种用于统一管理有限的常量数据的数据类型。它将常量设置为对象,提高了代码的可读性和简洁性。通过使用枚举类型,可以在代码中更方便地读取和使用常量。
2.JDK1.5之前使用枚举![](https://i-blog.csdnimg.cn/blog_migrate/44673ac9608fd6bce7e3c2db8165789a.png)
演示:
/*
JDK1.5之前使用枚举
*/
public class MeiJuDemo1 {
public static void main(String[] args) {
Season spring = Season.SPRING;
System.out.println(spring);
}
}
//在JDK1.5之前,人们去编写一个枚举类的实现
class Season {
//1. 创建枚举类的属性(成员变量),必须是作为私有常量出现
private String name;
private String info;
//2. 必须将构造方法私有化,这是为了保证类的对象是有限个的目的
private Season(String name, String info) {
this.name = name;
this.info = info;
}
//3. 提供公共的静态的final方法给外界获取枚举类中多个对象
public static final Season SPRING = new Season("春天", "春暖花开");
public static final Season SUMMER = new Season("夏天", "夏日炎炎");
public static final Season AUTUMN = new Season("秋天", "秋高气爽");
public static final Season WINTER = new Season("冬天", "银装素裹");
//4. 提供公共的获取属性的方法
public String getName() {
return name;
}
public String getInfo() {
return info;
}
//5. 重写toString()方法
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", info='" + info + '\'' +
'}';
}
}
3.JDK1.5之后使用枚举
演示:
/*
JDK1.5之后编写枚举类的方式
*/
public class MeiJuDemo2 {
public static void main(String[] args) {
Season2 spring = Season2.SPRING;
spring.fun1();
Season2 winter = Season2.WINTER;
winter.fun1();
}
}
interface Inter{
void fun1();
}
//java提供了一个关键字代替class, enum
//枚举类也可以实现接口
enum Season2 implements Inter{
// 提供公共的静态的final方法给外界获取枚举类中多个对象
//对象必须是作为枚举类中的第一个部分
// SPRING("春天","春暖花开"), //等同于public static final Season SPRING = new Season("春天","春暖花开");
// SUMMER("夏天","夏日炎炎"),
// AUTUMN("秋天","秋高气爽"),
// WINTER("冬天","银装素裹");
//如果枚举类实现了一个接口,针对不同的有限个对象,自己可以有着不同的实现
SPRING("春天","春暖花开"){
@Override
public void fun1() {
System.out.println("好好学习,天天向上!");
}
},
SUMMER("夏天","夏日炎炎"){
@Override
public void fun1() {
System.out.println("游泳");
}
},
AUTUMN("秋天","秋高气爽"){
@Override
public void fun1() {
System.out.println("吃水果");
}
},
WINTER("冬天","银装素裹"){
@Override
public void fun1() {
System.out.println("堆雪人");
}
};
//1. 创建枚举类的属性(成员变量),必须是作为私有常量出现
private String name;
private String info;
//2. 必须将构造方法私有化,这是为了保证类的对象是有限个的目的
private Season2(String name,String info){
this.name = name;
this.info = info;
}
//3. 提供公共的获取属性的方法
public String getName() {
return name;
}
public String getInfo() {
return info;
}
//4. 重写toString()方法
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", info='" + info + '\'' +
'}';
}
}