Lambda表达式

Lambda表达式

1、函数式接口

1. 函数式接口当接口只有一个抽象方法时才能实现。

  • 必须只有一个抽象方法才能使用,需要注意的是如果接口中有Object类中的抽象方法,那么这个抽象方法不算该接口本身的方法。
  • 比如Comparator接口中虽然含有compare和equals两个抽象方法,但equals方法在Object类中是默认实现的,所以Comparator接口还是会满足函数式接口的使用条件。

2. Lambda表达式是返回一个某个实现了接口方法的对象,其中包含了方法体的具体实现方式,可以通过返回的对象直接调用接口中的抽象方法,如:

import org.junit.Test;
public class Java8Lambda01 {
    @Test
    public void Test02(){
        /*
         * Lambda表达式做法
         * -> 是Lambda表达式的操作符
         * 箭头左边是接口抽象方法的参数列表,run方法没有形参,就用一个()代替
         * 箭头右边是在run方法中实现的内容,这里是打印一句话
         */
        Runnable runnable = () -> System.out.println("你目前看过的番中感触最深的是?");
        runnable.run();
    }
    
    @Test
    public void Test01(){
        //这里演示一下普通做法
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("我们无法一起学习");
            }
        };
        runnable.run();
    }
}

可以看出Lambda函数式接口的功能强大之处,正常情况下,是需要实现类去实现接口中的抽象方法,然后才能调用该方法。但上例中却直接由一个MathOperation接口的引用调用了该接口的抽象方法,这是很简便的。

3. Lambda表达式会根据上下文推断出应该返回什么对象,如:

public void LambdaTest2{
    public static void main(String[] args){
        /*
        *这里构造一个匿名对象,Thread构造器中需要传递一个Runnable类型的参数
        *此时Lambda表达式根据上下文知道这里是返回一个Runnable实现类的对象
        */
        new Thread( () -> System.out.println("Hello!") ).start();
    }
}

4. Java内置的4大核心函数式接口

接口消费型接口:Consumer函数型接口:Function<T,R>断定型接口:Predicate供给型接口:Supplier
方法void accept(T t)R apply(T t)boolean test(T t)T get()

这些接口都是功能性接口,使用了 @FunctionalInterface标注。了解这些接口,以后如果有需求时就可以直接应用函数式接口,不必再定义自己的接口。此外在java.util.function 包下还有其他大量的功能性接口。

2、方法引用

方法引用也是 JDK 8增加的功能,属于Lambda表达式的变式,主要有3种方式:

Object::instanceMethod		//对象::实例方法
Class::staticMethod			//类名::静态方法
Class::instanceMethod		//类名::实例方法(难!与众不同)

可以看到方法引用是通过 :: 来分隔类名(或对象名)与方法名的。具体用法如下:

import org.junit.Test;
import java.util.function.Consumer;
public class Java8Lambda02 {
    @Test
    public void Test03(){
        /*
         *本例中使用的方法:
         *Consumer ---> void accept(T t)
         *PrintStream ---> void println(T t)
         */
        
        /*Lambda表达式*/
        Consumer<String> consumer = str -> System.out.println(str);
        consumer.accept("看过很多云");
        
        /*
          *方法引用---对象::实例方法
          *要求:接口方法、类实例方法的返回类型和参数列表要相同
          *注意:System.out返回的是PrintStream对象!!!
          *本例可以写成:Consumer<String> consumer = System.out::println;
          */
        PrintStream ps = System.out;
        Consumer<String> consumer = ps::println;
        consumer.accept("却只爱过你");
    }
}
    @Test
    public void Test04(){
        /*
         *本例中使用的方法:
         *Function ---> R apply(T t)
         *Math ---> Long round(Double d)
         */

        /*Lambda表达式*/
        Function<Double, Long> fun1 = aDouble -> Math.round(aDouble);
        System.out.println(fun1.apply(4.5));

        /*
         *方法引用---类::静态方法
         *要求:接口方法、类静态方法的返回类型和参数列表要相同
         *本例中泛型自己设置成相同的类型和参数
         */
        Function<Double, Long> fun2 = Math :: round;
        System.out.println(fun2.apply(4.5));
    }
 	@Test
    public void test06(){
        /*
         *使用的方法:
         * Comparator ---> int compare(T t1, T t2)
         * String ---> int t1.compareTo(t2)
         */

        /*Lambda表达式*/
        Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
        System.out.println(com1.compare("abc", "abd"));

        /*
         * 方法引用---类::实例方法
         * 要求:返回类型相同,接口方法参数列表中的第一个变量,作为类实例方法的调用者
         */
        Comparator<String> com2 = String::compareTo;
        System.out.println(com2.compare("abc", "abd"));
    }

第三种方法引用比较难理解,再看一个例子:

    @Test
    public void test07() {
        /*
         *使用的方法:
         *Predicate ---> boolean test(T t)
         *String ---> boolean s.isEmpty()
         */
        
        /*匿名内部类形式*/
        Predicate<String> pre = new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.isEmpty();
            }
        };
        System.out.println(pre.test("123"));

        /*Lambda表达式*/
        Predicate<String> pre1 = s1 -> s1.isEmpty();
        System.out.println(pre1.test("123"));

        /*方法引用---类::实例方法*/
        Predicate<String> pre2 = String::isEmpty;
        System.out.println(pre2.test("123"));
    }

从第四4个例子也可以看出,方法引用像是Lambda表达式的变形,而Lambda表达式又是匿名内部类的简化,使用Lambda表达式可以让我们编写的代码更加简洁。

3、构造器引用

构造器引用与方法引用差不多,也是属于Lambda表达式的语法糖。通过 类名::new 来实现。

先构造一个示例类Teacher:

public class Teacher {
    static int i = 1;   //方便实现编号自动+1
    private int id;
    private String name;

    public Teacher(){
        id = -1;
        name = null;
        System.out.println("无参构造器被调用啦!!!");
    }

    public Teacher(String name){
        this.id = i++;
        this.name = name;
    }

    @Override
    public String toString() {
        return id + " " + name;
    }

    public int getId() { return id; }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

下面来看构造器引用与匿名内部类、Lambda表达式的区别

public class NewTest {
    @Test
    public void test1(){
        /*
     	 *需要用到的接口方法
     	 * Supplier ---> T get()
    	 */
        
        /*方式1:匿名内部类*/
        Supplier<Teacher> sup1 = new Supplier<Teacher>() {
            @Override
            public Teacher get() {
                return new Teacher();  //返回无参构造器
            }
        };
        sup1.get();

        /*方式2:Lambda表达式*/
        Supplier<Teacher> sup2 = () -> new Teacher();
        sup2.get();

        /*方法3:构造器引用*/
        Supplier<Teacher> sup3 = Teacher::new;
        sup3.get();
    }    
}

上例调用的是无参构造器,再来看看如何调用含参的构造器:

	@Test
    public void test2(){
        /*
         *需要用到的接口方法
         *Function<T,R> ---> R apply(T t)
         */

        /*方式1:匿名内部类*/
        Function<String, Teacher> fun1 = new Function<String, Teacher>() {
            @Override
            public Teacher apply(String s) {
                return new Teacher(s);
            }
        };
        Teacher tc1 = fun1.apply("黎老师");
        System.out.println(tc1);

        /*方式2:Lambda表达式*/
        Function<String, Teacher> fun2 = s1 -> new Teacher(s1);
        Teacher tc2 = fun2.apply("许老师");
        System.out.println(tc2);

        /*方式3:构造器引用*/
        Function<String, Teacher> fun3 = Teacher::new;
        Teacher tc3 = fun3.apply("欧老师");
        System.out.println(tc3);
    }

从上面两个例子中可以看到,实现构造器引用需要:

  • 找到一个有返回类型的泛型接口(只含一个抽象方法),保证返回的类型能设置成相应的类名;
  • 接口方法的参数有多少个,就能调用相同参数个数的构造器。如上两例中 get() 调用无参,apply(T t) 调用只含一个参数的构造器,它们都有返回值,返回类型为Teacher。
4、数组引用

数组引用与含一个参数的构造器引用几乎一样,传入数组的长度,返回相应数组的类型,如:

    @Test
    public void Test3(){
        /*匿名内部类*/
        Function<Integer, Teacher[]> fun1 = new Function<Integer, Teacher[]>() {
            @Override
            public Teacher[] apply(Integer integer) {
                return new Teacher[integer];
            }
        };
        Teacher[] teachers1 = fun1.apply(3);
        System.out.println(Arrays.toString(teachers1));

        /*Lambda表达式*/
        Function<Integer, Teacher[]> fun2 = integer -> new Teacher[integer];
        Teacher[] teachers2 = fun1.apply(6);
        System.out.println(Arrays.toString(teachers2));

        /*数组引用*/
        Function<Integer, Teacher[]> fun3 = Teacher[]::new;
        Teacher[] teachers3 = fun1.apply(9);
        System.out.println(Arrays.toString(teachers3));
    }

上面例子都采取不断简化的语法,先用常见的匿名内部类,再到Lambda表达式,再到各种引用。这样做是方便对比,可以看出要使用Lambda表达和方法引用、构造器引用、数组引用,都要了解接口抽象方法的参数列表和返回类型 。 这些接口除了能有和Object类相同的方法外,只含一个抽象方法(如Comparator接口中的equals方法,虽然在接口中也是抽象方法,但要排除在外),但可以含有其他默认方法和静态方法。


这章知识点我看书和视频好久,但一直都感觉没掌握好,只能先行将已经学会的知识点记下,以后有机会再仔细看看核心技术卷1的内容,希望各位大佬对笔记中有错的部分多加指点,不胜感激!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值