lambda表达式
演变(阶段一)
阶段一:new接口的实现类
有一个接口MyInterface,该接口里有一个test方法。
public interface MyInterface {
void test();
}
public class MyImplement implements MyInterface {
@Override
public void test() {
}
}
我们以前的做法是new一个该接口MyInterface的实现类MyImplement。如此才能创建该接口实现类的对象。
//以前的写法:
MyInterface myInterface=new MyImplement();
myInterface.test();
演变(阶段二)
阶段二:使用匿名内部类
//匿名内部类:
MyInterface myInterface=new MyInterface(){
@Override
public void test(){
}
};
现在可以少写一个.java文件,即不再创建类MyImplement.java
注意java里接口是不能new的,那我们下面等号右边new MyInterface()是什么呢?
new的是MyInterface接口的实现类。人家后面还带个{},,来势汹汹的样子
演变(阶段三)
阶段三:使用lambda表达式
lambda本质就是函数式接口的实例
所谓函数式接口就是只有一个抽象方法的接口。使用@FunctionalInterface
//lambda表达式
MyInterface myInterface = () -> {};
- ->左边,是一个形参列表。如果只有一个参数,括号"()"可以去掉不写
- ->右边,是lambda体,函数式接口中抽象方法的实现。
- 如果只有一条语句,{}和return 也可以省略。
- lambda参数列表的数据类型可以省略不写。即类型推断
lambda语法格式
1、接口要求
口诀:拷贝小括号,写死右箭头,落地花括号
作用:简化接口对象的创建
能使用lambda表达式的接口要求:
1.该接口中抽象方法才可以使用lambda处理
2.该接口只能有一个抽象方法(多个抽象方法lambda无法进行类型推断,会报错)
3.该接口中允许存在多个default方法(java8以后)
4.该接口中允许存在多个static方法(java8以后)
无参数、无返回值
语法格式一: 无参数、无返回值。
典型:Runnable接口run方法
//Runnable 接口如下
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
public static void main(String[] args) {
Runnable r2 = () -> System.out.println("我爱北京天安门");
r2.run();
}
1个参数,无返回值
语法格式二: 有1个参数,无返回值。
典型:Consumer接口的accept()
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
public static void main(String[] args) {
Consumer<String> c = s -> System.out.println(s);
c.accept("母亲节快乐");
}
2个参数,有返回值
语法格式三:2个参数,有返回值。Comparator接口compare方法
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
public static void main(String[] args) {
//创建函数接口Comparator的实例
Comparator<Integer> com2 = (o1, o2) -> (Integer.compare(o1, o2));
System.out.println(com2.compare(32, 21));//1
}
函数式接口
- Consumer void accept(T t)
- Supplier<T> T get();
- Function<T, R> R apply(T t)
- Predicate<T> boolean test(T t);
- BiFunction<T, U, R> R apply(T t, U u);
1、Consumer<T> : 消费型接口
//Consumer<T> : 消费型接口
@Test
public void test1(){
happy(648.0, (d) -> System.out.println("开服第一天消费" + d + "元"));
}
public void happy(double money, Consumer<Double> con){
con.accept(money);
}
Collection接口的方法forEach
void forEach(Consumer<? super E> action)
使用
// 使用forEach()结合匿名内部类迭代
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.forEach(str -> {
if (str.length() > 3)
System.out.println(str);
});
2、Supplier<T>:供给型接口
@Test
public void test05(){
//在调用方法时指定使用什么样的策略去产生10个整数
List<Integer> list = getValue(10, () -> (int) (Math.random() * 100));
for (Integer num : list) {
System.out.println(num);
}
}
public List<Integer> getValue(int n, Supplier<Integer> sup){
// 供给型接口 生产整数并存入集合中
List<Integer> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
// 策略模式,添加什么样的整数由策略模式Supplier接口的get方法指定
list.add(sup.get());
}
return list;
}
removeIf:删除容器中所有满足filter
指定条件的元素,
boolean removeIf(Predicate<? super E> filter)
// 使用removeIf()结合Lambda表达式实现
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.removeIf(str -> str.length()>3); // 删除长度大于3的元素
3、Function<T, R> : 函数型接口
//Function<T, R> : 函数型接口
//需求:处理字符串
@Test
public void test1(){
String str1 = strHandler("abcde", (s) -> s.toUpperCase());
System.out.println(str1);
System.out.println("----------------------------------");
String s2 = strHandler("我在精神病院斩神!", (s) -> s.substring(2, 5));
System.out.println(s2);
}
public String strHandler(String str, Function<String, String> fun){
return fun.apply(str);
}
4、Predicate<T> : 断言型接口
需求:过滤集合中的元素
@Test
public void test06(){
List<String> list = Arrays.asList("java", "php", "python", "c#", "go");
//在调用时传入指定的策略模式,使用哪种方式来过滤字符串集合
List<String> strList = filterList(list, (s) -> s.length() > 2);
for (String s : strList) {
System.out.println(s);
}
}
public List<String> filterList(List<String> list, Predicate<String> pre){
//需求:过滤集合中的元素,怎么过滤由策略模式断言接口的test方法决定
List<String> newList = new ArrayList<>();
for (String s : list) {
if(pre.test(s)){
newList.add(s);
}
}
return newList;
}
方法引用:
当lambda体中的内容,只有一个方法调用(包括构造方法)时,可以使用方法引用。
(方法引用是lambda表达式的另外一种表现)
方法引用中的“方法”的参数列表与返回值类型,必须与函数式接口中抽象方法的参数列表与返回值类型保持一致!
格式一、对象的引用:: 实例方法名
通过函数式接口Consumer的抽象方法accept推断方法println的类型为一个参数无返回值。
- void accept(T t);
- void println(String x)
@Test
public void test1(){
PrintStream ps = System.out;
Consumer<String> con = (s) -> ps.println(s);
System.out.println("----------------------------");
//通过函数式接口的抽象方法推断方法的类型。
Consumer<String> con2 =ps::println;
con2.accept("夜幕小队,谢幕演出");
}
格式二、类名::静态方法名
通过函数式接口Comparator的抽象方法compare推断方法compare方法为2个参数有返回值。
int compare(T o1, T o2) int compare(int x, int y)
@Test
//类名 :: 静态方法名
public void test1(){
Comparator<Integer> com = (o1, o2) -> Integer.compare(o1, o2);
System.out.println("----------------------------------");
//方法引用第二种方式
Comparator<Integer> com2 =Integer::compare;
System.out.println(com2.compare(10,20));//-1
}
格式三、类名::实例方法名
若函数式接口中抽象方法的第一个参数,是 Lambda 体中方法的调用者,第二个参数(或无参),是 Lambda 体中方法的参数时可以使用 类名::实例方法名
现在好像是不能再用上面那俩种方式来推断了。。。经过我自己的测试[待求证],格式三方法引用中类名是调用者的类或其父类。也就是<>左边那个。 boolean test(T t, U u); public boolean equals(Object anObject);
情况1:
BiPredicate是一个函数式接口,表示接受两个变量返回一个boolean类型的值。
@Test
//类名 :: 实例方法名
public void test1(){
BiPredicate<String, Integer> bp = (s1, s2) -> s1.equals(s2);
System.out.println("----------------------------------------------");
BiPredicate<String, String> bp1 = String::equals;
System.out.println(bp1.test("abc", "abc"));//true
}
情况2:
//类名 :: 实例方法名
@Test
public void test5(){
Function<Employee, String> fun = (e) -> e.getName();
System.out.println("------------------------------");
// Function<T, R> R apply(T t)
// 类名是调用者e的类Employee或其父类
Function<Employee, String> fun2 = Employee::getName;
System.out.println(fun2.apply(new Employee("张三", 18)));
}
构造器引用: 类名::new
函数式接口中抽象方法的参数列表必须与构造器参数列表保持一致
格式一:无参构造器,Supplier<T> T get();
@Test
// 类名::new
public void test1(){
Supplier<Employee> sup = () -> new Employee();
System.out.println("--------------------------------");
//构造器引用
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
}
格式二:2个参数的有参构造器,BiFunction<T, U, R> R apply(T t, U u);
@Test
// 类名::new
public void test1(){
BiFunction<String, Integer, Employee> bf = (s, i) -> new Employee(s, i);
System.out.println("---------------------------------------");
BiFunction<String, Integer, Employee> bf2 = Employee::new;
Employee emp = bf2.apply("张三", 18);
System.out.println(emp);
}
数组引用 : 数组类型::new
public void test1(){
Function<Integer, String[]> fun = (n) -> new String[n];
System.out.println("--------------------------------------");
//创建长度为10的字符串数组
Function<Integer, String[]> fun2 = String[]::new;
String[] strs = fun2.apply(10);
System.out.println(strs.length);
}
Optional容器类
思考:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法。我们首先要判断这个返回值是否为null,只有在非空的前提下才能将其作为其他方法的参数。
一、 创建一个Optional 实例
public void test1() {
// Optional.empty() : 创建一个空的 Optional 实例
Optional<Object> optional = Optional.empty();
//isPresent() : 判断容器是否为空
if(!optional.isPresent()){
System.out.println("1");//用empty创建的空 Optional 实例是可以调用isPresent方法的。类似于空字符串,所以会打印1
System.out.println(optional.get());//空容器获取这里会报异常NoSuchElementException
}
//Optional.of(T t) : 创建一个 Optional 实例
Optional<Object> optional1 = Optional.of(null);//参数为null在运行时会抛出空指针异常
//Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例 Optional.ofNullable=Optional.of+Optional.empty
Optional.ofNullable(null);//传null则相当于调用Optional.empty,传的不是null则调用Optional.of
}
二、获取 Optional容器中的对象
orElse(T t) 和 orElseGet(Supplier s)
get();//如果Optional容器有值则将其返回,否则抛出NoSuchElementException。
orElseGet方法区别于orElse,可以接受Supplier接口的实现用来生成默认值
@Test
public void test1() {
// 创建一个 Optional 实例
Optional<Employee> op1 = Optional.ofNullable(null);
Optional<Employee> op2 = Optional.ofNullable(new Employee("张三", 18));
System.out.println(op2);
// orElse(T t) : 如果调用对象包含值,返回该值,否则返回t 和get方法比较的话
Employee emp = op2.orElse(new Employee("李四", 20));
System.out.println(emp);
// orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
Employee emp1 = op1.orElse(new Employee("李四", 20));
System.out.println(emp1);
System.out.println("--------------------------------");
// orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
Employee emp2 = op2.orElseGet(() -> {
Employee e = new Employee("王五", 35);
return e;
});
System.out.println(emp2);
// orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
Employee emp3 = op1.orElseGet(() -> {
Employee e = new Employee("王五", 35);
return e;
});
System.out.println(emp3);
}
map和flatMap
@Test
public void test2() {
Optional<Employee> op2 = Optional.ofNullable(new Employee("张三", 18));
System.out.println(op2);
// map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
Optional<String> op = op2.map((e) -> e.getName());
System.out.println(op.get());//张三
// flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
Optional<String> op3 = op2.flatMap((e) -> Optional.ofNullable(e.getName()));
System.out.println(op3.get());
}