目录
- 后面会增加在生产环境中的实际使用
- Cousumer(?super integer):里面的泛型要么是integer或者integer的父类
1.为何需要Lambda表达式
-
在Java中,我们无法将函数作为一个参数传递给一个方法也无法声明返回一个函数的方法
-
而在JavaScript中,函数参数是一个函数,返回值是另一个函数的情况是非常常见的,JavaScript是一个非常典型的函数式语言
-
Lambda表达式是一种匿名函数,他没有声明的方法,即没有访问修饰符,返回值声明和方法名
-
Java中Lambda表达式是对象,他们必须依附在一类特别的对象函数—函数式接口
-
作用:传递行为而不仅仅是值
-
提升抽象层次
-
Api重用性更好
-
更加灵活
-
1.1 Lambda的格式
-
Lambda表达式的使用: 举例:(o1,o2) -> integer.compare(o1,o2)
->: lambda操作符或者箭头操作符
左边 : lambda形参列表(接口中的抽象方法的形参列表)
右边: lambda体(重写的抽象方法的方法体)
-
lambda表达式的使用(分为六种情况的介绍)
-
格式一:无参,无返回值
()->{ sout(“ 输出一句话”) };
-
格式二:lambda需要一个参数,但是没有返回值
(string str) ->{ sout(“输出参数str ”)}
-
格式三:数据类型可以省略,编译器可推断
(str) ->{sout(“ ”)}
-
格式四:lambda只需要一个参数时,参数的小括弧可以省略
str ->{ sout(“ ”)}
-
格式五:lambda需要两个或以上的参数,多条执行语句,并且有返回值
(x,y) ->{
sout(“ ”);
Return integet.compare(x,y);
};
-
格式六:
lambda体只有一条执行语句可能是return语句,return与大括号若有,可以省略
(x,y) -> integet.compare(x,y)
-
2.函数式接口
-
高阶函数:如果一个函数接受一个函数作为参数,或者返回一个函数作为返回值,那么该函数就叫做高阶函数,下面中的compute就是一个高阶函数
public int compute (int a, Function<Integer,Integer> function){
return function.apply(a);
}
-
该接口只有一个抽象方法
-
在该接口上声明了FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口(只有一个抽象方法).
-
一个接口只有一个抽象方法,但我们并没有给该接口声明FunctionalInterface注解,那么编译器依旧会将该接口看作是函数式接口
-
函数式接口最好加上@functioninterface注解,会检查是否符合规范
-
注意:如果有一个抽象方法重写了object中的public方法,抽象方法不会加一(defalut方法也不会加一)
接口 | 参数 | 返回类型 | 描述 |
---|---|---|---|
Predicate<T> | T | boolean | 用于判断参数是否符合要求 |
Consumer<T> | T | void | 用于接受一个参数但不提供返回值(打印) |
Function<T,R> | T | R | 接受一个对象转成不同类型的对象 |
Supplier<T> | None | T | 提供一个对象 |
UnaryOperator<T> | T | T | 接收对象并返回同类型的对象 |
BinaryOperatpr<T> | (T,T) | T | 接受两个同类型的对象,并返回一个原类型对象 |
2.1 Predicate<T>
代表了一个参数的判断:匹配了判断返回true(返回true的结果会被保存下来)反之返回false(返回false的结果会被丢弃).在集合和流中会得到大量的应用
2.2 Consumer<T>
- 接受一个参数无返回值
2.3 Function<T,R>
list.stream().map(String ::toUpperCase) == >item -> item.toUpperCase())
Function<String, String> function = String::toUpperCase;
Touppercase有一个返回值类型string 对应第二个泛型string;调用Touppercase的对象:item
类引用一个实例方法对应的第一个参数就是调用实例方法的对象
functionTest.compute(2, value -> { return value * 2; });
value -> { return value * 2; } //该行为传递给function
function.apply(a) //执行:value * 2
public int compute(int a, Function<Integer,Integer>function){
return function.apply(a);
}
Function默认的两个方法
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
//返回的是一个组合函数首先对输入应用before函数,再对之前的结果应用本函数
//输入什么返回什么
static <T> Function<T,T>identity(){
return t->t;
}
BiFunction**:接受两个参数返回一个结果
default <V> BiFunction<T,U,V> andThen(Function<? super R, ? extends T> after) {
Objects.requireNonNull(before);
return (T t,U u) -> after.apply(apply(t,u));
}
//apply(t,u):调用BiFunction的apply需要两个输入的参数(t,u)返回一个结果;调用after的apply方法接受一个参数返回一个参数
- Demo
- 易错点: BiFunction<Integer,List<Person>,List> biFunction List里面不能缺少泛型
- getPersonsByAge方法不够灵活,如果此时增加一个<=传入的age,需要再次新增一个方法
- getPersionsByAge2方法把具体的实现交给调用者,不用在新增方法
2.4 Supplier<T>
不接受参数,返回一个参数(创建类)
2.5BinaryOperatpr<T>
3.Optional
Optional解决空指针问题
-
基于值的类,例如
java.util.Optional``java.time.LocalDateTime
基于值的如果程序试图区分对基于值的类的相等值的两个引用,无论是直接通过引用相等还是间接通过诉诸同步、身份散列、序列化或任何其他身份敏感机制,它可能会产生不可预知的结果。在基于值的类的实例上使用这种对身份敏感的操作可能会产生不可预知的影响,应该避免。
-
是最终的和不可变的(尽管可能包含对可变对象的引用);
-
具有、 和的实现
equals
, 这些实现仅根据实例的状态计算,而不是从其标识或任何其他对象或变量的状态计算;hashCode``toString
-
不使用身份敏感操作,例如
==
实例之间的引用相等 ()、实例的身份哈希码或实例的内在锁上的同步; -
仅基于 被认为相等
equals()
,而不是基于引用相等 (==
); -
没有可访问的构造函数,而是通过工厂方法实例化,这些方法不承诺返回实例的身份;
-
在相等时可以自由替换,这意味着在任何计算或方法调用中交换任何两个实例
x
并且y
相等的实例equals()
不应该产生可见的行为变化。
-
- Optioal通过empty构造,一个空的optional
- Of 保证参数不能为空,否则出现异常调用者要保证参数不能为空
- OfNullable:参数可能为空也可能不为空
- Ifpresent:判断对象是否存在
- Get()取值
- Optional对象不能new 里面的构造方法是私有的,通过静态方法生成实例
- 重要的方法map:
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
ObjectsrequireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
//map方法本身又返回一个optional对象,把一个值映射成另一个值functonal对象(一个输入,一个输出)
/**
* map和flatMap的使用
* 主体还是第一个stream,第二个stream和用来做过滤
*/
List<String> tempList = new ArrayList<String>(){{
add("张三0");
}};
Optional.ofNullable(list2).ifPresent(var -> {
List<String> list3 = var.stream().flatMap(x -> tempList.stream().filter(y -> x.getName().equals(y)).map(var1 -> x.getName())).collect(Collectors.toList());
list3.stream().forEach(var2 -> {
/**
* 输出:
* 张三0
*/
System.out.println(var2);
});
}
);
- 不要将optional作为方法参数,也不要再类中声明optiona为成员变量,optional只作为方法的返回值用来规避空
- Optional要使用函数式编程风格。
4.方法引用
4.1方法引用和lambda之间的关系
方法引用是lambda的一个语法糖(更简练的言语表达较复杂的含义);使用lambda能够使用的功能,恰巧有一个功能能够表示出来,实现出来,就可以通过方法引用替换lambda表达式;lambda方法体的实现正好是某一个恰好存在的方法提供的功能
-
Demo
students.forEach(item -> System.out.println(item)) ----> students.forEach(System.out ::println);
我们可以将方法引用作为一个指针,指向另外一个函数(System.out.println(item))
-
使用情景
-
当要传递给lambda体的操作,已经有实现的方法了,可以使用方法引用
-
4.2格式
类(对象)::方法名
4.2.1对象名(引用名)::非静态方法(实例方法名)
4.2.2 类名::静态方法
-
//传统的lambda表达式(compareStudentByScore()是Student中的方法)
-
sort(Comparator(<? E supre E> c))
-
Comparator是一个函数式接口: int compare(T o1, T o2) 接受两个参数,返回一个整型
-
-
students.sort( (studentparam1,studentparam2) -> Student.compareStudentByScore(studentparam1,studentparam2));
-
//方法引用 students.sort(Student::compareStudentByScore);
Lambda表达式的方法体恰好有一个相同功能的方法是客观存在的(compareStudentByScore必须要存在而且是Student类的一个方法)
4.2.3类::实例方法名(非静态方法)
-
Student类中的方法
-
//方法引用第三种---情况 类::实例方法名(非静态方法) students.sort(Student ::compareByName);
-
compareByName是一个实例方法一定是由对象调用的:lambda表达式的第一个参数作为调用者调用compareByName,其余的作为参数传递进入
-
一个字符串的类型为String,将之转换为大写: String::toUpperCase(输入参数是方法的调用者)
-
Collections对集合names排序
-
Collections.sort(names,(String o1,String o2) -> {
return o2.compareTo(o1);
})
-
4.2.4 构造方法引用
-
类名::new ====调用构造方法来生成一个新的对象
4.2默认方法的一些内容
-
MyInterFace1和MyInterFace2有两个同名的默认方法
-
如果MyClass类同时实现了MyInterFace1和MyInterFace2:编译器不知道取哪个接口里面的默认方法,这种情况下必须显示的告诉编译器
-
如果MyInterface1有一个实现类,MyInterface2没有实现类,MyClass继承了MyInterface1的实现类同时实现了MyInterface2这个接口,MyClass中的myMethod方法取MyInterface2中的默认接口还是MyInterface1的实现类的方法
-
上面的那种方式编译器不会报错:取MyInterface1的实现类中的myMethod方法(实现类的优先级比接口的优先级更高)
-