一、方法引用
1.理解
lambda表达式是对函数式接口的一种匿名实现,也就是入参、出参都符合的情况下,加了函数体。
如果函数体只是调用了已有的方法,此时入参、出参、函数体3者都确定了,只需要把要调用的方法描述清楚即可,提供调用者(4种)、方法名即可,此时就可以用方法引用了,是lambda表达式的进一步简化。
于是,根据方法调用者的4种情况,有4种写法。
-
静态方法引用 : ClassName :: staticMethodName
没啥好说的,提供类名+方法名,参数自动补上 -
构造器引用 : ClassName :: new
没啥好说的,提供类名+方法名,参数自动补上 -
类的任意对象的实例方法引用: ClassName :: instanceMethodName
类的任意对象指的是由入参中传入的对象(第一个,后面的无论几个都是参数)来调用,也就是说,入参要比调用的方法的参数多一个。这种情况下,调用者是不确定的,调用的时候才确定
Arrays.sort(strs,(s1,s2)->s1.compareToIgnoreCase(s2)); //sort的参数比compareToIgnoreCase要多一个,多出来的就是调用者 Arrays.sort(strs, String::compareToIgnoreCase);
看到ClassName :: instanceMethodName,就表示,args0是调用者,后面的args是instanceMethodName的参数,相当于(args0,args...) -> args0.instanceMethodName(args...),args0是ClassName 类型的
- 入参的数量由接口决定,跟数据无法,比如前一个里面元素是map,那后面的方法引用也只能有一个入参。也就是说,一个运算符里面的函数类型是规定好的,方法引用写啥方法是有限制的,方法引用只是lambda的简化,功能远没有lambda强大。
比如下图,第二行就报错,因为方法引用无法传入自定义的入参;只能像第三行那样,调用一个无参的方法,除非接口支持,比如Arrays.sort,就支持2个参数
-
特定对象的实例方法引用 : object :: instanceMethodName
特定对象指的是在lambda外面创建的对象
public class Test { public static void main(String[] args) { Test test = new Test(); // lambda表达式使用: Arrays.asList(new String[] {"a", "c", "b"}).stream().forEach(s -> test.println(s)); // 特定对象的实例方法引用: Arrays.asList(new String[] {"a", "c", "b"}).stream().forEach(test::println); } public void println(String s) { System.out.println(s); } }
2.注意
- 无法显式传参
3.example
3.1 任意对象
类的任意对象指的是由入参中传入的对象(第一个,后面的无论几个都是参数)来调用,也就是说,入参要比调用的方法的参数多一个。这种情况下,调用者是不确定的,调用的时候才确定
public class Student{
private String name;
private Integer score;
public void setNameAndScore(String name, Integer score)
{
this.name = name;
this.score = score;
System.out.println("Student "+ name +"'s score is " + score);
}
public static void main(String[] args)
{
/*lambda表达式的用法:
TestInterface testInterface = (student, name, score) -> student.setNameAndScore(name, score);*/
//用变量接收lambda表达式,相当于给匿名函数起了个名
TestInterface testInterface = Student::setNameAndScore;
testInterface.set(new Student(), "DoubleBin", 100);
}
@FunctionalInterface
interface TestInterface
{
// 注意:入参比Student类的setNameAndScore方法多1个Student对象,除第一个外其它入参类型一致
public void set(Student d, String name, Integer score);
}
}
3.2 特定对象
test对象是lambda外创建的
public class Test
{
public static void main(String[] args)
{
Test test = new Test();
// lambda表达式使用:
Arrays.asList(new String[] {"a", "c", "b"}).stream().forEach(s -> test.println(s));
// 特定对象的实例方法引用:
Arrays.asList(new String[] {"a", "c", "b"}).stream().forEach(test::println);
}
public void println(String s)
{
System.out.println(s);
}
}
stream API
二、Optional
1.com
null这个概念,本身就有争议,它使得方法的返回值变得更复杂。需要做非空判断,而Optional就是把非空判断以及后续操作给封装了(相当于在任意操作前加个为null的判断)
那都有哪些操作呢?
1、为null时返回默认值,不为null时返回自身–orElse
2、为null时返回默认值,不为null时调用方法–map()
3、为null时抛出异常,不为null时返回自身–orElseThrow()
4、为null时返回false,不为null时返回true–isPresent()
5、为null时不执行,不为null时执行代码–ifPresent()
6、为null时返回empty,否则传入predicate进行过滤
。。。。。。
众多场景都有对应的api提供支持
2.创建(包装)
// 1、创建一个包装对象值为空的Optional对象
Optional<String> optStr = Optional.empty();
// 2、创建包装对象值非空的Optional对象
Optional<String> optStr1 = Optional.of("optional");
Optional.of(null);//直接会报错,用来处理不允许为null的场景,用try-catch处理
// 3、创建包装对象值允许为空的Optional对象
Optional<String> optStr2 = Optional.ofNullable(null);
3.map
这个map()的源码,注意入参是一个函数,出参还是Optional,这就意味着后续可以继续调用api,比如orElse(),注意orElse()的入参跟泛型有关。
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) //判断value是否为null,也就是Optional包装的值
return empty(); //返回EMPTY,这意味着后续会触发orElse()的缺省值
else {
return Optional.ofNullable(mapper.apply(value)); //mapper是个实现类,apply()方法就是传入的逻辑
}
}
函数式
将一段逻辑作为一等公民,参数、返回值。
根据场景,将这些逻辑分为:
生产:无参,只有返回值
消费:有参,无返回值
判断:有一个入参,返回boolean