Java8新特性 Lambda表达式 (二)方法引用和构造器引用

前言

     上一节介绍了Lambda表达式的基础用法,Java为我们提供了很多函数式接口。详见我转载的一篇博客:深入学习Java8中的函数式接口

    接着,是配合Lambda表达式使用的新语法:方法引用构造器引用。

引用类方法 类名::类方法 函数式接口中被实现的方法的全部参数传给该类方法作为参数 (a,b...)->类名.类方法(a,b...)
引用特定对象的实例方法 特定对象::实例方法 函数式接口中被实现方法的全部参数传给该方法作为参数 (a,b...)->特定对象.实例方法(a,b...)
引用某类对象的实例方法 类名::实例方法 函数式接口中被实现的方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数 (a,b...)->a.实例方法(b...)
引用构造器 类名::new 函数式接口中被实现方法的全部参数传给该构造器作为参数 (a,b...)->new 类名(a,b...)

方法引用

        若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用,(可以将方法引用理解为 Lambda 表达式的另外一种表现形式),方法引用分为三种:

        对象 ::实例方法

        类名 ::类方法

        类名 ::实例方法

    方法引用的前提所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!

   先看对象 ::实例方法:        

//首先,这是Consumer接口,accept方法接收一个T,无返回值
@FunctionalInterface
public interface Consumer {
    void accept(T var1);
}
    @Test
    public void test1(){
        //1  为了更好理解,将ps声明出来
        PrintStream ps = System.out;
        //2  传入的参数用来打印到控制台
        Consumer con = (str) -> ps.println(str);
        //3  调用方法
        con.accept("Hello World!");
        //4  上面是一个简单的例子,下面我们换个写法
        System.out.println("--------------------------------");
        //5  ps为PrintStream对象,println为实例方法
        Consumer con2 = ps::println;
        //6  调用方法
        con2.accept("Hello Java8!");
        //7  所以也可换成这种写法
        //Consumer con3 = System.out::println;
    }

运行结果:


再看类名 ::类方法:    

//这是BiFunction接口,apply方法,接收两个参数,返回一个参数
@FunctionalInterface
public interface BiFunction {
    R apply(T var1, U var2);
}
    @Test
    public void test2(){
        //1  使用Math类的静态方法max(double,double)做最大值选择
        BiFunction fun = (x, y) -> Math.max(x, y);
        //2  调用方法,返回double
        Double apply1 = fun.apply(1.5, 22.2);
        System.out.println(apply1);

        System.out.println("--------------------------------------------------");

        //3  Max.max(x,y)写成 Math::max
        BiFunction fun2 = Math::max;
        Double apply = fun2.apply(1.2, 1.5);
        System.out.println(apply);
    }
运行结果:


最后是类名 ::实例方法

    如果是调用实例方法,这种写法不能显式的看到调用者,那应该怎么写呢?    

//BiPredicate接口,接收两个参数,返回boolean
@FunctionalInterface
public interface BiPredicate {
    boolean test(T var1, U var2);
}
    @Test
    public void test3(){
        //1  x调用equals方法,y作为参数
        BiPredicate bp = (x, y) -> x.equals(y);
        System.out.println(bp.test("abcde", "abcde"));

        System.out.println("-----------------------------------------");

        //2  可以改写为 String::equals
        BiPredicate bp2 = String::equals;
        System.out.println(bp2.test("abc", "abc"));
    }

运行结果:

这种String : : equals写法不是很好理解,equals方法的调用者在哪呢?

这就是关键,BiPredicate接口的test方法有两个参数,第一个参数就是调用者,第二个参数就是被调用方法的参数

如果,BiPredicate的test方法只有一个参数,那会是怎么样?

我很快写了个例子:

//这是我自己的一个函数式接口,只有一个参数
@FunctionalInterface
public interface TestMethodRefInterface {
    boolean test(T a);
}
//我想实现判断开头是不是以http://开头字符串的方法(此代码是不能通过的)
TestMethodRefInterface test = String::startsWith;
System.out.println(test.test("http://"));

这时候就明了了,在泛型对应的情况下,如果被调用方法参数个数和函数式接口方法参数一致,会被认为是类名 ::静态方法,因为没有指定调用者嘛,所以startWith不是静态方法就不会通过。


构造器引用

    构造器引用可以理解为另一种形式的类名 ::类方法名。    

//Supplier接口的get方法,没有参数,返回T
@FunctionalInterface
public interface Supplier {
    T get();
}
    @Test
    public void test4(){
        //1  新建一个对象
        Supplier sup = () -> new Employee();
        System.out.println(sup.get());

        System.out.println("------------------------------------");

        Supplier sup2 = Employee::new;
        System.out.println(sup2.get());
    }

运行结果:



    Lambda表达式简化了代码,下节介绍Lambda表达式与匿名内部类的区别!

你可能感兴趣的:(Java)