lambda

457 阅读9分钟

1. lambda

1. 什么是Lambda表达式

Lambda是一个匿名函数,即没有函数名的函数(简化了匿名委托的使用,让你让代码更加简洁)

2. Lambda表达式实例

//匿名内部类
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.print("hello toroot");
    }
};

//lambda
Runnable r2 = ()->System.out.print("hello toroot");

//匿名内部类
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return Long.compare(o1.length(),o2.length());
    }
});

//lambda
TreeSet<String> ts2 = new TreeSet<>((o1,o2)-> Long.compare(o1.length(),o2.length()));

3. Lambda 表达式语法

Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或剪头操作符。

它将 Lambda 分为两个部分:

a. 左侧:指定了 Lambda 表达式需要的所有参数

b. 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。

4. Lambda 表达式语法格式

1. 无参数,无返回值

   public  void test1() {
        //一、语法格式一:无参数,无返回值
        Runnable runnable =() -> System.out.println("Hello Lambda!");
   }

2. 有一个参数,并且无返回值

    public  void test2() {
        //语法格式二:有一个参数,并且无返回值
        Consumer consumer =  (x) -> System.out.println(x);

    }

3. 若只有一个参数,小括号可以省略不写

    public  void test3() {
        //语法格式三:若只有一个参数,小括号可以省略不写
        Consumer consumer =  x-> System.out.println(x);
    }

4. 有两个以上的参数,有返回值,并且 Lambda 体中有多条语句

    public  void test4() {
        //语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
        Comparator<Integer> com = (x, y) -> {
            System.out.println("函数式接口");
            return Integer.compare(x, y);
        };
    }

5. 若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写

    public  void test5() {
        //语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    }

6. Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”

    public  void test6() {
        //语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
        Comparator<Integer> com =  (Integer x, Integer y) -> Integer.compare(x, y);

        Comparator<Integer> com2 =  (Integer x, Integer y) -> Integer.compare(x, y);
    }

4. 函数式接口 ( @FunctionalInterfac )

Lambda表达式需要“函数式接口”的支持

函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。 可以使用注解 @FunctionalInterface 修饰可以检查是否是函数式接口

@FunctionalInterface 标记在接口上,“函数式接口“是指仅仅只包含一个抽象方法的接口,加上注释,写两个方法,IDE会报错。


@FunctionalInterface
public interface MyFun<T> {
    public T getValue(T t);
}

public static void main(String[] args) {
    String newStr = toUpperString((str)->str.toUpperCase(),"toroot");
    System.out.println(newStr);
}

public static String  toUpperString(MyFun<String> mf,String str) {
    return mf.getValue(str);
}

5. Java内置函数式接口

全部的函数式接口可以查看 Java 8 函数式接口 这边抽取几个进行讲解

接口 参数 返回类型 示例
Predicate T boolean 这是个美女吗?
Consumer T void 输出一个值
Function<T,R> T R 获得 Person对象的名字
Supplier None T 工厂方法
UnaryOperator T T 逻辑非 (!)
BinaryOperator (T, T) T 求两个数的乘积 (*)

1.Predicate<T>

Predicate<T> 接受一个输入参数,返回一个布尔值结果。

/**
     * 断言型接口:Predicate<T>
     */
    public void test1 () {
        List<Integer> l = new ArrayList<>();
        l.add(102);
        l.add(172);
        l.add(13);
        l.add(82);
        l.add(109);
        List<Integer> list = filterInt(l, x -> (x > 100));
        for (Integer integer : list) {
            System.out.println(integer);
        }
    }

    /**
     * 过滤集合
     * @param list
     * @param pre
     * @return
     */
    public List<Integer> filterInt(List<Integer> list, Predicate<Integer> pre){
        List<Integer> l = new ArrayList<>();
        for (Integer integer : list) {
            if (pre.test(integer))
                l.add(integer);
        }
        return l;
    }
102
172
109

2. Consumer<T>

Consumer<T> 代表了接受一个输入参数并且无返回的操作

    /**
     * 消费型接口Consumer<T>
     */
    public void test2 () {
        consumo(500, (x) -> System.out.println(x));
    }

    public void consumo (double money, Consumer<Double> c) {
        c.accept(money);
    }
500.0

3. Function<T, R>

Function<T,R> 接受一个输入参数,返回一个结果。

/**
     * 函数型接口:Function<R, T>
     */
    public void test3 () {
        String s = strOperar(" asdf ", x -> x.substring(0, 2));
        System.out.println(s);
        String s1 = strOperar(" asdf ", x -> x.trim());
        System.out.println(s1);
    }

    /**
     * 字符串操作
     * @param str 需要处理得字符串
     * @param fun Function接口
     * @return 处理之后得字符传
     */
    public String strOperar(String str, Function<String, String> fun) {
        return fun.apply(str);
    }
 a
asdf

4. Supplier<T>

Supplier<T> 无参数,返回一个结果。

	/**
     * 供给型接口,Supplier<T>
     */
    public void test4() {
        Random ran = new Random();
        List<Integer> list = supplier(10, () -> ran.nextInt(10));

        for (Integer i : list) {
            System.out.println(i);
        }
    }

    /**
     * 随机产生sum个数量得集合
     * @param sum 集合内元素个数
     * @param sup
     * @return
     */
    public List<Integer> supplier(int sum, Supplier<Integer> sup){
        List<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < sum; i++) {
            list.add(sup.get());
        }
        return list;
    }
5
1
2
0
8
6
1
1
4
8

5. UnaryOperator<T>

UnaryOperator<T> 接受一个参数为类型T,返回值类型也为T。

    /**
     * UnaryOperator<T>
     */
    public void test5() {
        String endStr = unaryOperator("hello", (t) -> t+" zeking");
        System.out.println(endStr);
    }

    /**
     * 转大写 操作
     * @param str
     * @param u
     * @return
     */
    public String unaryOperator(String str, UnaryOperator<String> u) {
        return u.apply(str.toUpperCase());
    }
HELLO zeking

6. BinaryOperator<T,T>

BinaryOperator<T,T> 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果

    /**
     * BinaryOperator<T>
     */
    public void test6(){
        int result = binaryOperator(5,6,(x,y)->{
            return x*y;
        });
        System.out.println(result+"");
    }

    public int binaryOperator(int x ,int y,BinaryOperator<Integer> b){
        return b.apply(x+1,y+2);
    }

48

6. 方法引用( :: )

1. 介绍

  • 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!) 方法引用:使用操作符 “ ::” 将方法名和对象或类的名字分隔开来。 如下三种主要使用情况 :
  • 对象 :: 实例方法
  • 类 :: 静态方法
  • 类 :: 实例方法

2. 什么时候可以用 ::方法引用(重点)

在我们使用Lambda表达式的时候,”->”右边部分是要执行的代码,即要完成的功能,可以把这部分称作Lambda体。有时候,当我们想要实现一个函数式接口的那个抽象方法,但是已经有类实现了我们想要的功能,这个时候我们就可以用方法引用来直接使用现有类的功能去实现

3. 示例

Person.java

public class Person {

    private String name;
    private Integer age;
    private Integer score;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public Person(String name, Integer age, Integer score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

   ...... //  省略 get  set 方法

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
    public static void main(String... args) {

        Person p1 = new Person("Zeking", 18, 60);
        Person p2 = new Person("David", 38, 1);
        Person p3 = new Person("Lucky", 28, 99);

        List<Person> personList = new ArrayList<>();
        personList.add(p1);
        personList.add(p2);
        personList.add(p3);

        //这里我们需要比较list里面的person,按照年龄排序
        //那么我们最常见的做法是
        //sort(List<T> list, Comparator<? super T> c)
        //1. 因为我们的sort方法的第二个参数是一个接口,所以我们需要实现一个匿名内部类
        Collections.sort(personList, new Comparator<Person>() {
            @Override
            public int compare(Person person1, Person person2) {
                return person1.getAge().compareTo(person2.getAge());
            }
        });
        //2. 因为第二个参数是一个@FunctionalInterface的函数式接口,所以我们可以用lambda写法
        Collections.sort(personList, (person1,person2) -> p1.getScore().compareTo(p2.getAge()));
        //3. 因为第二个参数我们可以用lambda的方式去实现,
        // 但是刚好又有代码(Comparator.comparing)已经实现了这个功能
        // 这个时候我们就可以采用方法引用了
        /**
         * 重点:
         * 当我们想要实现一个函数式接口的那个抽象方法,但是已经有类实现了我们想要的功能,
         * 这个时候我们就可以用方法引用来直接使用现有类的功能去实现。
         */
        Collections.sort(personList, Comparator.comparing(Person::getAge));

        System.out.println(personList);
    }
[Person{name='Zeking', age=18, score=60}, Person{name='Lucky', age=28, score=99}, Person{name='David', age=38, score=1}]

4. 对象 :: 实例方法

直接调用任意对象的实例方法,如 obj::equals 代表调用 obj 的 equals 方法与接口方法参数比较是否相等,效果等同 obj.equals(t);。

当前类的方法可用this::method进行调用,父类方法同理。

Person p1 = new Person("Zeking", 18, 60);
Person p2 = new Person("David", 38, 1);

Predicate<Person> p = p1::equals;
System.out.println(p.test(p2));  //  false

5. 类 :: 静态方法

直接调用某类的静态方法,并将接口方法参数传入,

如Android中 TextUtils::isEmpty,效果等同 TextUtils.isEmpty(s);

TextUtils.isEmpty("ss");

Predicate<CharSequence> p = TextUtils::isEmpty;
boolean s = p.test("");
System.out.println(s);   // true

BinaryOperator<Double> bo = (n1,n2) ->Math.pow(n1,n2);
BinaryOperator<Double> bo2 = Math::pow;
System.out.println(bo2.apply(3.0,2.0)); // 9.0
public static void main(String[] args) {
    Consumer<String>  c = x->System.out.println(x);
    //等同于
    Consumer<String> c2 = System.out::print;
    c2.accept("hello zeking");  // hello zeking
}

6. 类 :: 实例方法

较为特殊,将接口方法参数列表的第一个参数作为方法调用者,其余参数作为方法参数。由于此类接口较少,故选择 Java 提供的 BiFunction 接口作为示例,该接口方法接收一个 T1 类对象和一个 T2 类对象,通过处理后返回 R 类对象:

BiFunction<String,String ,Boolean> biFunction = String::equals;
System.out.println( biFunction.apply("a","b")); //false

7. 构造器引用( :: )

格式: ClassName :: new

与函数式接口相结合,自动与函数式接口中方法兼容。

可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!

PersonNewInterface.java

@FunctionalInterface
public interface PersonNewInterface<A,B,C,D> {
    D apply(A a ,B b,C c);
}
    public static void main(String... args) {
		// 无参构造
        Supplier<Person> x = () -> new Person();
        Supplier<Person> x2 = Person::new;
        Person p_x2 = x2.get();
        System.out.println(p_x2); // Person{name='null', age=null, score=null}

		// 一个参数
        Function<String,Person> f  = y->new Person(y);
        Function<String,Person> f2 = Person::new;
        Person f2_person = f2.apply("Lucky");
        System.out.println(f2_person); // Person{name='Lucky', age=null, score=null}

		// 3个参数  自定义函数式接口PersonNewInterface
        PersonNewInterface<String, Integer, Integer, Person> pni = (a,b,c)->new Person(a,b,c);
        PersonNewInterface<String, Integer, Integer, Person> pni2 = Person::new;
        Person pni_person = pni2.apply("ZekingLee", 23, 77);
        System.out.println(pni_person); // Person{name='ZekingLee', age=23, score=77}
    }

8. 数组引用( :: )

格式: type[] :: new

public static void main(String... args) {
		Function<Integer,Person[]> f  = x->new Person[x];
        Function<Integer,Person[]>  f2 = Person[]::new;

        Person[] f_person = f2.apply(2);
        System.out.println(f_person.length);  // 2
    }