JDK1.8新特性(部分)【Lambda表达式、函数式接口】--学习JavaEE的day41

day41

JDK1.8新特性

JDK1.8新特性简介

  • 速度更快 - 优化底层源码,比如HashMap、ConcurrentHashMap
  • 代码更少 - 添加新的语法Lambda表达式
  • 强大的Stream API
  • 便于并行
  • 最大化减少空指针异常 - Optional

Lambda表达式

简介

Lambda是一个匿名函数(方法), 允许把函数作为一个方法的参数 。利用Lambda表达式可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

一般都是优化匿名内部类

基础语法

无参数、无返回值的抽象方法

java 复制代码
public class Test1 {
	@Test
	public void test01() {
//		I1 i1 = new I1() {
//			@Override
//			public void method() {
//				System.out.println("传统使用匿名内部类的方式");
//			}
//		};
//		i1.method();

//		I1 i1 = ()->{
//			System.out.println("使用lambda表达式的方式");
//		};
//		i1.method();
        
        //lambda表达式里面只有一个语句情况,大括号可以省略进行简化代码
		I1 i1 = ()-> System.out.println("采用Lambda表达式的方式");
		i1.method();
	}
}
interface I1{
	public void method();//无参数、无返回值的抽象方法
}

一个参数、无返回值的抽象方法

外部new匿名内部类调用方法时,参数看接口定义的规范里的参数类型

java 复制代码
public class Test01 {
	public static void main(String[] args) {
		
//		I1 i1 = new I1() {
//			@Override
//			public void method(String str) {
//				System.out.println("使用传统匿名内部类的方式:" + str);
//			}
//		};
//		i1.method("过往云烟");
		
//		I1 i1 = (String str)->{
//			System.out.println("使用lambda表达式的方式:" + str);
//		};
//		i1.method("过往云烟");
		
//		I1 i1 = (String str)->System.out.println("使用lambda表达式的方式:" + str);
//		i1.method("过往云烟");
		
		
//		I1 i1 = (str)->System.out.println("使用lambda表达式的方式:" + str);
//		i1.method("过往云烟");
		
		I1 i1 = str->System.out.println("使用lambda表达式的方式:" + str);
		i1.method("过往云烟");
	}
}
public interface I1 {

	public void method(String str);//一个参数、无返回值的抽象方法
}

多个参数、无返回值的抽象方法

java 复制代码
public class Test01 {
	public static void main(String[] args) {
		
//		I1 i1 = new I1() {
//			@Override
//			public void method(String str, int i) {
//				System.out.println("使用传统匿名内部类的方式:" + str + " -- " + i);
//			}
//		};
//		i1.method("过往云烟", 666);
		
//		I1 i1 = (String str,int i)->{
//			System.out.println("使用lambda表达式的方式:" + str + " -- " + i);
//		};
//		i1.method("过往云烟", 777);
		
//		I1 i1 = (String str,int i)->System.out.println("使用lambda表达式的方式:" + str + " -- " + i);
//		i1.method("过往云烟", 888);
		
		I1 i1 = (str, i)->System.out.println("使用lambda表达式的方式:" + str + " -- " + i);
		i1.method("过往云烟", 999);
		
	}
}
public interface I1 {
    //多个参数、无返回值的抽象方法
	public void method(String str,int i);
}

多个参数、有返回值的抽象方法

java 复制代码
public class Test01 {
	public static void main(String[] args) {
	
//		I1 i1 = new I1() {
//			@Override
//			public String method(int a, int b) {
//				return "使用传统匿名内部类的方式:" + (a+b);
//			}
//		};
//		String method = i1.method(10, 10);
//		System.out.println(method);
		
//		I1 i1 = (int a,int b)->{
//			return "使用lambda表达式的方式:" + (a+b);
//		};
//		String method = i1.method(20, 20);
//		System.out.println(method);
		
		I1 i1 = (a, b)-> "使用lambda表达式的方式:" + (a+b);
		String method = i1.method(30, 30);
		System.out.println(method);
		
	}
}
public interface I1 {
	//多个参数、有返回值的抽象方法
	public String method(int a,int b);
}
注意点
  1. 重写方法的形参只有一个时,可以不加小括号
  2. Lambda表达式当中不允许声明一个与局部变量同名的参数或者局部变量
  3. Lambda表达式中访问外层的局部变量,外层的局部变量自动变成隐式常量,默认添加final
  4. 重写方法的形参同时加类型或同时不加类型
java 复制代码
public class Test1 {
	@Test
	public void test01() {
		int x;
		int num = 10;
		I1 i1 = x -> System.out.println(x + (num++));
        i1.method(1000);
		
		I2 i2 = (int x,int y) -> {
			int result = x+y;
			return result;
		};
		int result = i2.method(10, 20);
		System.out.println(result);
	}
}
interface I1{
	public void method(int num1);
}
interface I2{
	public int method(int num1,int num2);
}
案例
案例1

1.调用Collections.sort()方法,通过定制排序比较两个Student对象(先按年龄比较,年龄相同按照薪资比较),使用Lambda表达式作为参数传递
注意:比较时注意不能用减,因为doublue减后的小数会在转int损失精度,ps:0.2变为0

java 复制代码
public class Test01 {

	public static void main(String[] args) {
		
		List<Student> stuList = Arrays.asList(
				new Student("张三", 28, 4800,Course.JAVA),
				new Student("李四", 36, 7200,Course.JAVA),
				new Student("王五", 19, 9600,Course.HTML),
				new Student("赵六", 42, 6100,Course.HTML),
				new Student("孙七", 23, 9600,Course.PYTHON),
				new Student("吴八", 28, 3000,Course.PYTHON));
		
//		Collections.sort(stuList, new Comparator<Student>() {
//			@Override
//			public int compare(Student o1, Student o2) {
//				if(o1.equals(o2)){
//					return 0;
//				}
//				
//				int compare = Integer.compare(o1.getAge(), o2.getAge());
//				if(compare != 0){
//					return compare;
//				}
//				
//				return Double.compare(o1.getSalary(), o2.getSalary());
//			}
//		});
		
		Collections.sort(stuList, (o1,o2)->{
			if(o1.equals(o2)){
				return 0;
			}
			
			int compare = Integer.compare(o1.getAge(), o2.getAge());
			if(compare != 0){
				return compare;
			}
			
			return Double.compare(o1.getSalary(), o2.getSalary());
		});
		
		for (Student stu : stuList) {
			System.out.println(stu);
		}
		
	}
}
public enum Course{//课程枚举
	JAVA,HTML,PYTHON;
}
public class Student{//学生类
	
	private String name;
	private int age;
	private double salary;
	private Course course;
    //有参、无参、get、set方法【略】
@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((course == null) ? 0 : course.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		long temp;
		temp = Double.doubleToLongBits(salary);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (course != other.course)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
			return false;
		return true;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("Student [name=");
		builder.append(name);
		builder.append(", age=");
		builder.append(age);
		builder.append(", salary=");
		builder.append(salary);
		builder.append(", course=");
		builder.append(course);
		builder.append("]");
		return builder.toString();
	}

}
案例2

2.创建I1接口,创建抽象方法:public String getValue(String str),在测试类中编写方法使用接口作为参数,将一个字符串转为大写,并作为方法的返回值

java 复制代码
public class Test01 {
    
	public static void main(String[] args) {
		
		String str = method("abc", (x)-> {
			return x.toUpperCase();
		});
		System.out.println(str);
		
		
	}
	
	public static String method(String str,I1 i1){
		return i1.getValue(str);
	}
}

interface I1{
	public String getValue(String str);
}
案例3

3.创建I1<T,R>接口,泛型T为参数,R为返回值,创建抽象方法:public R add(T t1,T t2),在测试类中编写方法使用接口作为参数,计算两个long类型的和

java 复制代码
public class Test01 {

	public static void main(String[] args) {
		
		long addLong = addLong(100,200,(a,b)->{
			return a+b;
		});
		System.out.println(addLong);
		
		
	}
	
	public static long addLong(long l1,long l2,I1<Long,Long> i1){
		return i1.add(l1, l2);
	}
}
interface I1<T,R>{
	public R add(T t1,T t2);
}

函数式接口

简介

函数式接口是指仅仅只包含一个抽象方法的接口,jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。配合Lambda表达式一起使用

四大核心函数式接口
函数式接口 参数类型 返回类型 用途
Consumer 消费型接口 T void void accept(T t);
Supplier 供给型接口 void T T get();
Function<T, R> 函数型接口 T R R apply(T t);
Predicate 断言型接口 T boolean booelan test(T t);
BiConsumer<T, U> T,U void 对类型为T,U参数应用操作。包含方法为void accept(T t,U u);
BiFunction<T, U, R> T,U R 对类型为T,U参数应用操作,并返回R类型的结果。包含方法为R apply(T t,U u);
UnaryOperator extends Function<T, T> T T 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为T apply(T t);
BinaryOperator extends BiFunction<T,T,T> T,T T 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(T t1,T t2);
ToIntFunction ToLongFunction ToDoubleFunction T int long double 分别计算int、long、double值的函数
IntFunction LongFunction DoubleFunction int long double R 参数为int、long、double类型的函数

应用场景:当项目中需要一个接口,并且该接口中只有一个抽象方法,就没必要去创建新的接口,直接选择Java提供的使用合适的函数式接口即可

案例替换

对Lambda表达式案例,使用合适的函数式接口去替换I1
创建I1接口,创建抽象方法:public String getValue(String str),

在测试类中编写方法使用接口作为参数,将一个字符串转为大写,并作为方法的返回值

注意:使用合适的函数式接口去替换I1

函数和方法:前端习惯称函数,后端习惯称方法,简单理解一个意思但非区别

函数式接口,一个抽象方法,超过就会报错

函数式接口替换接口,就案例来看更改方法里的接口,用函数调用方法,少一层

java 复制代码
public class Test01 {

	public static void main(String[] args) {
		
		String str = method("abc", (x)->{
			return x.toUpperCase();
		});
		System.out.println(str);
		
		
	}
	
	public static String method(String str,Function<String, String> fun){
		return fun.apply(str);
	}
}
public class Test02 {

	public static void main(String[] args) {
		
		String str = method("abc", (x)->{
			return x.toUpperCase();
		});
		System.out.println(str);
		
		
	}
	
	public static String method(String str,UnaryOperator<String> uo){
		return uo.apply(str);
	}
}

创建I1<T,R>接口,泛型T为参数,R为返回值,创建抽象方法:public R add(T t1,T t2),

在测试类中编写方法使用接口作为参数,计算两个long类型的和

java 复制代码
public class Test01 {

	public static void main(String[] args) {
		
		long addLong = addLong(100,200,(a,b)->{
			return a+b;
		});
		System.out.println(addLong);
		
		
	}
	
	public static long addLong(long l1,long l2,BiFunction<Long, Long, Long> bf){
		return bf.apply(l1, l2);
	}
}
public class Test02 {
	public static void main(String[] args) {
		
		long addLong = addLong(100,200,(a,b)->{
			return a+b;
		});
		System.out.println(addLong);
		
		
	}
	
	public static long addLong(long l1,long l2,BinaryOperator<Long> bo){
		return bo.apply(l1, l2);
	}
}

总结

1.lambda表达式(其实就有优化匿名内部类的方案)

注意:使用lambda表达式必须有多态的思想

2.函数式接口

该接口中只有一个抽象方法

@FunctionInterface这个注解表示该接口是函数式接口

经验:

如果你想玩转lambda表达式,必须先去学习匿名内部类+多态

函数式接口应用场景:需求要你写个接口,这个接口只有一个抽象方法,就用函数式接口去代替

相关推荐
小李不想输啦3 小时前
什么是微服务、微服务如何实现Eureka,网关是什么,nacos是什么
java·spring boot·微服务·eureka·架构
张铁铁是个小胖子3 小时前
微服务学习
java·学习·微服务
ggs_and_ddu3 小时前
Android--java实现手机亮度控制
android·java·智能手机
AITIME论道4 小时前
论文解读 | EMNLP2024 一种用于大语言模型版本更新的学习率路径切换训练范式
人工智能·深度学习·学习·机器学习·语言模型
敲代码娶不了六花4 小时前
jsp | servlet | spring forEach读取不了对象List
java·spring·servlet·tomcat·list·jsp
Yhame.4 小时前
深入理解 Java 中的 ArrayList 和 List:泛型与动态数组
java·开发语言
是小崔啊6 小时前
开源轮子 - EasyExcel02(深入实践)
java·开源·excel
myNameGL6 小时前
linux安装idea
java·ide·intellij-idea
青春男大6 小时前
java栈--数据结构
java·开发语言·数据结构·学习·eclipse
HaiFan.7 小时前
SpringBoot 事务
java·数据库·spring boot·sql·mysql