java8 API - lambda表达式 方法引用
新特性的优点
-
速度更快
-
代码更少(Lambda表达式)
-
强大的Stream API
-
便于并行
-
最大化减少空指针异常:Optional
-
Nashorn引擎,允许在JVM上运行JS应用
Lambda表达式
是一个匿名函数,可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)
本质
作为接口的具体实现类——函数式接口
如何使用
(o1, o2) -> Integer.compare(o1, o2);
-
->
:Lambda操作符 -
操作符左边:Lambda形参列表,其实就是接口中抽象方法的形参列表
-
操作符右边:Lambda体,重写接口抽象方法的方法体
总结:
-
操作符左边:lambda形参列表的参数类型可以省略(类型推断),如果lambda形参列表只有一个参数,其一对()也可以省略
-
操作符右边:lambda体应该使用一对{}包裹,如果lambda体只有一条执行语句(可能是return语句),可以省略{}和return关键字
语法格式一:无参无返回值
Runnable r1 = new Runnable(){
@Override
public void run(){
sout("runnable1");
}
};
r1.run();
//runable接口就一个抽象方法无参无返回值
Runnable r2 = () -> {sout("runable2")};
r2.run();
语法格式二:Lambda需要一个参数,但是没有返回值
Consumer<String> c1 = new Consumer<>(){
@Override
public void accept(String s){
sout(s);
}
};
c1.accpet("con1");
//Consumer接口就一个抽象方法有参无返回值
Consumer<String> c2 = (String s) -> {sout(s)};
c2.accpet("con2");
语法格式三:数据类型可以省略,可由编译器推断得出,称类型推断
//推断出s是string类型的省略
Consumer<String> c3 = (s) -> {sout(s)};
c3.accpet("con3");
语法格式四:只需要一个参数,参数的小括号可以省略
Consumer<String> c4 = s -> {sout(s)};
c4.accpet("con4");
语法格式五:需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> c1 = new Comparator<>(){
@Override
public int comparTo(Integer o1, Integer o2){
sout(o1);
sout(o2);
return o1 - o2;
}
};
Comparator<Integer> c2 = (Integer o1, Integer o2) -> {sout(o1), sout(o2), return o1 - o2};
//或者直接省略类型
Comparator<Integer> c3 = (o1, o2) -> {sout(o1), sout(o2), return o1 - o2};
语法格式六:当Lambda体只有一条语句,return与大括号若有,都可以省略
Comparator<Integer> c1 = (o1, o2) -> {return o1 - o2};
Comparator<Integer> c2 = (o1, o2) -> o1 - o2;
函数式接口
-
只包含一个抽象方法的接口,就是函数式接口
-
可以通过Lambda表达式来创建该接口的对象(作为接口的具体实现类——函数式接口),若lambda表达式抛出非运行时一场,该异常需要在目标接口的抽象方法上进行声明
-
可以在接口上使用@FunctionalInterface注解,可以检验它是否是一个函数式接口
-
在java.util.function包下定义了Java8的丰富的函数式接口
java内置四大核心函数式接口
函数式接口 | 参数类型 | 返回值 | 用途 | 包含方法 |
---|---|---|---|---|
Consumer<T> 消费型接口 |
T | void | 对类型为T的对象应用操作,不返回值 | void accept(T t) |
Supplier<T> 供给型接口 |
无 | T | 不给参数,返回类型为T的对象 | T get() |
Function<T, R> 函数型接口 |
T | R | 对类型为T的对象应用操作,并返回R类型的对象 | R apply(T t) |
Predicate<T> 断定型接口 |
T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值 | boolean test(T t) |
public void happyTime(double money, Consumer<double> c){
c.accept(money);
}
//消费接口
public void test(){
happyTime(10.0, money -> sout(money))
}
//传入一个字符串list,通过断言接口判断这个list中是否包含指定的字符串(需要重写方法),如果有,就加入到新的list中返回
public List<String> containsChar(List<String> list, Predicate<String> p){
ArrayList<String> res = new ArrayList<>();
for(String s : list){
//s传入到predicate的test方法里
if(p.test(s)){
res.add(s);
}
}
return res;
}
//断定接口
public void test(){
List<String> result = containsChar(list, s -> s.contans("a"));
}
方法引用
-
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
-
方法引用可以看作是Lambda表达式深层次的表达,方法引用就是Lambda表达式,也是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖
***要求!
实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的参数列表和返回值类型保持一致
使用情景
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
使用情况
情况一:对象::非静态实例方法
如下代码,Consumer接口中的方法void accept(T t)
和PrintSteam中的方法void println(T t)
参数列表返回值都一样,也就是说,传递给lambda体的操作sout,已经和接口中的方法结构一样,使用方法引用,这里ps对象就是println执行需要的对象System.out
Consumer<String> con1 = str -> System.out.println(str);
con1.accpet("con1");
//创建调用方法引用的实例对象
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accpet("con2");
Supplier中的T get()
,和Emp中的String getName()
参数列表返回值都一样
Emp emp = new Emp("empname");
Supplier<String> s1 = () -> emp.getName();
sout(s1.get());
//方法引用
Supplier<String> s2 = emp::getName;
sout(s2.get())
情况二:类::静态方法
Comparator中的int compare(T t1, T t2)
和Integer中的static int compare(T t1, T, t2)
,由于是静态方法,所以需要用Integer类去调用
Comparator<Integer> c1 = (t1, t2) -> Integer.compare(t1, t2);
int num1 = c1.compare(1, 2);
Comparator<Integer> c2 = Integer:compare;
int num2 = c2.compare(2, 3);
Function<T,R>中的R apply(T t)
和Math中的static R round(T t)
,Math中的是静态方法,所以需要用类去调用
Function<Double, Long> f1 = d -> Math.round(d);
Long l1 = f1.apply(11.1);
Function<Double, Long> f2 = Math:round;
Long l2 = f2.apply(12.2);
情况三:类::非静态方法,两个参数变类+一个参数
Comparator中的int compare(T t1, T t2)
和String中的int t1.compareTo(t2)
,第一个方法2个参数,第二个方法调用者算一个参数,只不过需用调用者的这个String类进行引用,就是类+一个参数
Comparator<String> c1 = (s1, s2) -> t1.comparTo(s2);
int num1 = c1.compare("abc", "abd");
Comparator<String> c2 = String::compareTo;
int num2 = c2.compare("abe", "abf");
BiPredicate中的boolean test(T t1, T t2)
和String中的boolean t1.equals(t2)
,第一个方法2个参数,第二个方法调用者算一个参数,只不过需用调用者的这个String类进行引用,就是类+一个参数
BiPredicate<String> bp1 = (s1, s2) -> s1.equals(s2);
boolean b1 = bp1.test("abc", "abc");
Bipredicate<String> bp2 = String::equals;
boolean b2 = bp2.test("abc", "abc");
Function中的R apply(T t)
和Emp中的String getName()
,第一个方法2个参数,第二个方法调用者算一个参数,只不过需用调用者的这个Emp类进行引用,就是类+一个参数
Emp e1 = new Emp("abc");
Function<Emp, String> f1 = e -> e.getName();
String name1 = f1.apply(e1);
Emp e2 = new Emp("qwe");
Function<Emp, String> f2 = Emp::getName;
String name2 = f1.apply(e2);
构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致
要求
-
一个接口的方法无参有返回值,和一个类的无参构造器类似,方法的返回值就是返回这个类的实例对象
-
一个接口的方法有参有返回值,和一个类的有参构造器类似,并且参数个数和接口中方法的个数一致,方法返回值就是返回这个类的实例对象
格式
Clazz(提供构造器的那个类)::new
情况一:无参构造器
Supplier接口的T get()
方法无参,返回值为T,和Emp类的无参构造器Emp(){}
类似
Supplier<Emp> s1 = new Supplier<>(){
@Override
public Emp get(){
return new Emp();
}
};
Emp e = s.get();
Supplier<Emp> s1 = () -> new Emp();
Emp e1 = s1.get();
Supplier<Emp> s2 = Emp::new;
Emp e2 = e2.get();
情况二:有参构造器参数个数和接口中方法的参数个数一致
Function接口的R apply(T t)
和Emp类的有参构造Emp(int id)
Function<Integer, Emp> f = new Function<>(){
@Override
public Emp apply(int id){
return new Emp(id);
}
};
Emp e = f.apply(1);
Function<Integer, Emp> f1 = id -> new Emp(id);
Emp e1 = f1.apply(2);
Function<Integer, Emp> f1 = Emp::new;
Emp e3 = f2.apply(3);
Bifunction接口的R apply(T t, U u)
和Emp的有参构造Emp(int id, String empName)
Bifunction<Integer, String, Emp> bf = new BiFunction<>(){
@Override
public Emp apply(int id, String empName){
return new Emp(id, empName);
}
};
Emp e = bf.apply("1", "a");
Bifunction<Integer, String, Emp> bf1 = (id, empName) -> new Emp(id, empName);
Emp e1 = bf1.apply("2", "b");
Bifunction<Integer, String, Emp> bf2 = Emp::new;
Emp e2 = bf1.apply("3", "c");
数组引用
把数组看成一个特殊的类,与构造器引用使用方法类似
格式
Clazz[]::new
情况:接口方法的参数(Intger, Clazz[])个数、类比新建数组填入的大小(Integer)和返回的数组对象Clazz[]
Function接口中的R apply(T t)
和新建String数组new String[length]
Function<Integer, String[]> f = new Function<>(){
@Override
public String[] apply(Integer length){
return new String[length];
}
};
String[] strs = f.apply(12);
Function<Integer, String[]> f1 = length -> new String[length];
String[] strs1 = f.apply(13);
Function<Integer, String[]> f2 = String[]::new;
String[] strs2 = f.apply(14);