java lamdda 表达式
前言
由于工作中一直没有用过jdk8,最近升级了jdk到8了,自己不得不进行学习下jdk8新特性,虽然现在jdk已经到了14了,工作和学生学习是不一样的,学生学习喜欢最新的,但是工作就不是了以项目为重,稳定为先。。。
1.8主要新特性
Lambda 表达式
函数式接口
方法引用 / 构造器引用
Stream API
接口中的默认方法 / 静态方法
新时间日期 API
其他新特性
Lambda express 和 stream 应该是比较突出的2个特点,现在项目中我已经开始尝试使用这2个特性,发现代码能简化不少
Lambda express
匿名函数
内部类方式:
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("内部类方式");
System.out.println("java7 实现方式");
}
};
new Thread(runnable).start();
lambda 表达式
new Thread(() -> System.out.println("lambdaexpress")).start();
Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递);可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,是Java语言表达能力得到提升。
基本语法:3 部分
1 左侧:一个括号内用逗号分隔的形式参数,参数是函数式接口里面方法的参数
2 中间:一个箭头符号:->
3 右侧:方法体,可以是表达式和代码块,方法体函数式接口里面方法的实现,如果是代码块,则必须用{}来包裹起来,且需要一个return 返回值,但有个例外,若函数式接口里面方法返回值是void,则无需{}
口诀:
写死小括号,拷贝右箭头,落地大括号
左右遇一括号省
左侧推断类型省
语法格式
无参无返回值
方式一: 无参数 无返回值
* () -> System.out.println(“dddd”)
@Test
public void test2(){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("传统写法");
}
};
runnable.run();
System.out.println("============");
Runnable r = () -> System.out.println("lambda express");
r.run();
}
一个参数无返回值
@Test
public void test3(){
Consumer<String> consumer = (p) -> System.out.println(p);
consumer.accept("ddddd");
//()可以不写
Consumer<String> consumer1 = p -> System.out.println(p);
consumer1.accept("ddddd");
}
两个及以上的参数,有返回值
@Test
public void test4(){
Comparator<Integer> comparator1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
Comparator<Integer> comparator = (a,b)->{
System.out.println("比较接口");
return -Integer.compare(a,b);
};
Integer[] aa = {2,4,6,3,7,3,8,2};
Arrays.sort(aa,comparator1);
System.out.println(Arrays.toString(aa));
Arrays.sort(aa,comparator);
System.out.println(Arrays.toString(aa));
}
函数式接口
函数式接口是新增的一种接口定义。
用@FunctionalInterface修饰的接口叫做函数式接,或者,函数式接口就是一个只具有一个抽象方法的普通函数式接口,@FunctionalInterface可以起到校验的作用。
接口中只有一个抽象方法的接口 @FunctionalIterface
测试:
定义一个函数式接口:
@FunctionalInterface
public interface MyFun {
Integer count(Integer a,Integer b);
}
使用
public static void main(String[] args) {
MyFun myFun1 = (a, b) -> a + b;
MyFun myFun2 = (a, b) -> a - b;
MyFun myFun3 = (a, b) -> a * b;
MyFun myFun4 = (a, b) -> a / b;
System.out.println(myFun1.count(1,3));
System.out.println(myFun2.count(1,3));
System.out.println(myFun3.count(1,3));
System.out.println(myFun4.count(1,3));
}
Java内置四大核心函数式接口:
在JDK7中其实就已经有一些函数式接口了,比如Runnable、Callable、FileFilter等等。
在JDK8中也增加了很多函数式接口,比如java.util.function包。
比如这四个常用的接口:
那么Java8中给我们加了这么多函数式接口有什么作用?
上文我们分析到,一个Lambda表达式其实也可以理解为一个函数式接口的实现者,但是作为表达式,它的写法
其实是多种多样的,比如
- () -> {return 0;},没有传入参数,有返回值
- (int i) -> {return 0;},传入一个参数,有返回值
- (int i) -> {System.out.println(i)},传入一个int类型的参数,但是没有返回值
- (int i, int j) -> {System.out.println(i)},传入两个int类型的参数,但是没有返回值
- (int i, int j) -> {return i+j;},传入两个int类型的参数,返回一个int值
- (int i, int j) -> {return i>j;},传入两个int类型的参数,返回一个boolean值
答案已经明显了,Java8中提供给我们这么多函数式接口就是为了让我们写Lambda 表达式更加方便,当然遇到特殊情况,你还是需要定义你自己的函数式接口然后才能写对应的Lambda表达式。
消费型接口
@Test
public void test01(){
//Consumer
Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x);
//test
consumer.accept(100);
}
Supplier 供给型接口
格式:Supplier
T get();
传入参数,对参数进行操作,然后有返回值
@Test
public void test2(){
Supplier<String> s =() -> {
return "ddd";
};
System.out.println(s.get());
}
/**
* 供给型接口,供给功能如何实现
*/
@Test
public void test3() {
List<Integer> list = getNumList(10, () -> {
Integer a = (int)(Math.random() * 10);
return a;
});
list.stream().forEach(System.out::println);
}
/**
* 产生指定个数的整数
* @param n
* @return
*/
public static List<Integer> getNumList(Integer n, Supplier<Integer> supplier) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
list.add(supplier.get());
}
return list;
}
Function 函数型接口
格式:Function<T,R>
R apply(T t);
/**
* 函数型接口
* Function<T, R>
*/
@Test
public void test31() {
String str = strHandler("abcdefg", (x) -> {
return x.toUpperCase().substring(0, 5);
});
System.out.println(str);
}
/**
* 需求:用于处理字符串
*/
public static String strHandler(String str, Function<String, String> function) {
// 使用apply方法进行处理,怎么处理需要具体实现
return function.apply(str);
}
Predicate 断言型接口
格式:Predicate, 用于做一些判断
/**
* 断言型接口(把长度大于3的str过滤出来)
*/
@Test
public void test4() {
List<String> list = Arrays.asList("abc", "abcd", "df", "cgg", "aaab");
List<String> result = strPredict(list, (x) -> x.length() > 3);
result.forEach(item -> {
System.out.println(item);
});
}
/**
* 将满足条件的字符串,放入到集合中
*/
public static List<String> strPredict(List<String> list, Predicate<String> predicate) {
List<String> result = new ArrayList<>();
list.forEach(item -> {
if(predicate.test(item)) {
result.add(item);
}
});
return result;
}
接口的默认方法和静态方法
在JDK7中,如果想对接口Collection新增一个方法,那么你需要修改它所有的实现类源码(这是非常恐怖的),
在那么Java8之前是怎么设计来解决这个问题的呢,用的是抽象类,比如:
现在有一个接口PersonInterface接口,里面有1个抽象方法:
这种方式我们不管是mybatis还spring等框架中可以大量看到这样的架构
那么在Java8中支持直接在接口中添加已经实现了的方法,一种是Default方法(默认方法),一种Static方法(静
态方法)。
接口的默认方法
在接口中用default修饰的方法称为默认方法 。
接口中的默认方法一定要有默认实现(方法体),接口实现者可以继承它,也可以覆盖它。
接口的静态方法
在接口中用static修饰的方法称为静态方法。
调用方式:
接口.方法名 的方式
因为有了默认方法和静态方法,所以你不用去修改它的实现类了,可以进行直接调用。
public interface Person {
public String getName(String name);
default int getAge(){return 23;};
static void testStatic(){
System.out.println("测试接口的静态方法");
};
}
public class YellowPerson implements Person {
@Override
public String getName(String name) {
return name;
}
// @Override
// public int getAge() {
// return 34;
// }
public static void main(String[] args) {
Person person = new YellowPerson();
System.out.println(person.getName("dddd"));
System.out.println(person.getAge());
Person.testStatic();
}
}
方法引用
方法引用与构造器引用
方法引用
概念
若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”,可以理解为方法引用是Lambda表达式的另外一种表现形式
格式
对象::实例方法名
类::静态方法名
类::实例方法名
注意
Lambda体中,调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。
若Lambda参数列表中,第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用ClassName::method
对象::实例方法名
/**
* 格式: 对象::实例方法名
*/
public static void test() {
Consumer<String> consumer = (x) -> System.out.println(x);
// 使用方法引用完成(也就是上述的方法体中,已经有Lambda实现了,那就可以用方法引用的方式实现)
// 同时使用方法引用的时候,需要保证:参数列表和返回值类型相同
PrintStream ps = System.out;
Consumer<String> consumer2 = ps::println;
}
/**
* 格式: 对象::实例方法名
*/
public static void test2() {
Employee employee = new Employee("张三", 12, 5555);
Supplier<String> supplier = () -> employee.getName();
System.out.println(supplier.get());
// 使用方法引用
Supplier<String> supplier1 = employee::getName;
System.out.println(supplier1.get());
}
类::静态方法名
/**
* 类::静态方法名
*/
public static void test3() {
Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
// 使用方法引用
Comparator<Integer> comparator2 = Integer::compare;
}
类::实例方法名
/**
* 类::实例方法名
*/
public static void test4() {
// 比较
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abc", "abc"));
// 使用方法引用
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));
}
构造器引用
格式
ClassName::new
注意
需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致
代码
/**
* 构造器引用
*/
public static void test() {
Supplier<Employee> supplier = () -> new Employee("张三", 18, 13);
// 构造器引用(调用的无参构造器)
Supplier<Employee> supplier1 = Employee::new;
Employee employee = supplier1.get();
// 构造器引用(调用有参构造器,一个参数的)
Function<Integer, Employee> function = Employee::new;
Employee employee1 = function.apply(10);
System.out.println(employee1.getAge());
}
数组引用
格式
Type::new
public static void test() {
Function<Integer, String[]> function = (x) -> new String[x];
function.apply(20);
// 数组引用
Function<Integer, String[]> function1 = String[]::new;
String[] strArray = function1.apply(20);
System.out.println(strArray.length);
}