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);

构造器引用

方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致

要求

  1. 一个接口的方法无参有返回值,和一个类的无参构造器类似,方法的返回值就是返回这个类的实例对象

  2. 一个接口的方法有参有返回值,和一个类的有参构造器类似,并且参数个数和接口中方法的个数一致,方法返回值就是返回这个类的实例对象

格式

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);

posted on 2022-08-19 15:43  老菜农  阅读(53)  评论(0编辑  收藏  举报

导航