Java8新特性之双冒号::语法:方法引用与构造器引用

1 方法引用

lambda表达式是用来简化函数式接口的匿名内部类的一种函数式编程的语法。

当Lambda体的实现是通过调用一个现有的方法来完成功能的,并且该方法满足一些特殊条件时,则可以通过双冒号::来引用该方法替代Lambda表达式。 双冒号::称为方法引用操作符,而它所在的表达式被称为方法引用。方法引用是一个简化的lambda表达式。

格式:类或对象::方法名,两个:之间不能有空格

三种主要使用情况:

情况1:对象名::实例方法名

情况2:类名::静态方法名

情况3:类名::实例方法名

前提条件

(1)Lambda体中只有一条语句,并且是方法调用语句

(2)函数式接口的抽象方法的返回值类型 与 lambda体中的那个方法的返回值类型一致

(3)函数式接口的抽象方法的参数列表 与 lambda体中的那个方法的形参列表一致(针对情况1和情况2);或 函数式接口的抽象方法的形参列表的第一个形参是作为lambda体中调用方法的对象,其余的形参才是这个方法的形参列表(情况3,若只有一个参数,则这个参数是仍是方法调用的对象,方法无参数)

1.1 对象—引用实例方法

1.1.1 普通使用情况:

@Test
public void test1() {
    List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
    //lambda表达式写法
    //Consumer<String> consumer = s -> System.out.println(s);
    list.forEach(s -> System.out.println(s));

    //使用方法引用
    //Consumer<String> consumer = System.out::println;
    list.forEach(System.out::println);
}

上面代码中的forEach方法是Iterable接口在JDK8新增的默认方法,List集合继承了该方法,方法源码:

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

forEach方法的形参的类型是Consumer接口,它是一个函数式接口,只有一个抽象方法void accept(T t),可以使用Lambda表达式赋值。上面代码中的lambda表达式是通过调用一个现有的方法来完成的,是out对象的println()方法

Consumer接口对象 public void accept(T t)
PrintStream类的out对象 public void println(Object x)

accept方法和println方法的形参列表和返回值类型一致,因此可以用方法引用写法System.out::println替代lambda表达式写法s -> System.out.println(s),其中方法引用中的方法的参数是可以根据上下文推导的。

Lambda 中 传递的参数 一定是方法引用中 的那个方法可以接收的类型,否则会抛出异常

1.1.2 super或this引用实例方法

当存在继承关系时,如果需要引用的方法是父类中的成员方法,那么可以使用“super::成员方法”的格式来使用方法引用;如果需要引用的方法是本类中的成员方法,那么可以使用“this::成员方法”的格式来使用方法引用。

代码示例:

public class InfoTest {
    public static void main(String[] args) {
        Son son = new Son();
        son.call();
        son.call2();
    }
}

interface Info {
    void method();
}

class Father {
    public void show() {
        System.out.println("父类show方法");
    }
}

class Son extends Father {

    public void showInfo(Info info) {
        info.method();
    }

    @Override
    public void show() {
        System.out.println("子类show方法");
    }

    public void call() {
        //传入方法引用,调用的是本类的show方法
        showInfo(this::show);
    }

    public void call2() {
        //传入方法引用,调用的是父类的show方法
        showInfo(super::show);
    }
}

1.2 类—引用静态方法

例如:Comparator接口的int compare(T o1, T o2)抽象方法,它的重写方法对应的lambda体中调用Integer.compare(int x, int y)方法,它们都是有参有返回值的方法;Consumer接口的T get()抽象方法,它的重写方法对应的lambda体中调用Math.random()方法;它们都能用方法引用来替代。

代码示例:

@Test
public void test2() {
    Integer[] array = {10, 2, 8, 5, 15, 20, 12};
    //lambda表达式写法
    Arrays.sort(array, (i1, i2) -> Integer.compare(i1, i2));
    System.out.println(Arrays.toString(array));

    Supplier<Double> s1 = () -> Math.random();
    Double value = s1.get();
    System.out.println(value);

    //使用方法引用
    Arrays.sort(array, Integer::compare);

    Supplier<Double> s2 = Math::random;
    Double value2 = s2.get();
}

1.3 类—引用实例方法

函数式接口的抽象方法的形参列表的第一个形参是作为lambda体中调用方法的对象,其余的形参才是这个方法的形参列表,若只有一个参数,则这个参数是仍是方法调用的对象,方法无参数

代码示例1:忽略大小写,实现字符串数组排序

@Test
public void test3() {
    String[] array = {"abc", "eee", "AAA", "bbb", "ccc"};
    //lambda表达式写法  参数s1作为compareToIgnoreCase方法的调用者,参数s2作为compareToIgnoreCase方法的形参
    Arrays.sort(array, (s1, s2) -> s1.compareToIgnoreCase(s2));
    System.out.println(Arrays.toString(array));
    
    //使用方法引用
    Arrays.sort(array, String::compareToIgnoreCase);
}

代码示例2:

public class Student {
    private String name;
    private int age;
	
    //构造器/get/set
}
@Test
public void test4() {
    Student student = new Student("jack", 18);
    //lambda表达式写法
    Function<Student, String> func1 = s -> s.getName();
    String name = func1.apply(student);
    System.out.println(name);

    //使用方法引用
    Function<Student, String> func2 = Student::getName;
    System.out.println(func2.apply(student));
}

2. 构造器引用

2.1 类—引用构造器

构造器引用是一类特殊的方法引用,当Lambda表达式是创建一个对象,并且Lambda表达式形参与构造器的形参列表一致时,可用构造器引用替代lambda表达式

格式:类名::new

代码示例:

@Test
public void test5() {
    //lambda表达式写法
    Function<String, Student> func1 = name -> new Student(name);
    Student s1 = func1.apply("jack");
    System.out.println(s1.getName());
	//使用构造器引用
    Function<String, Student> func2 = Student::new;
    Student s2 = func2.apply("lily");
    System.out.println(s2.getName());
}

2.2 数组—引用构造器

当Lambda表达式是创建一个数组对象,并且Lambda表达式形参,正好是数组的长度时,可用构造器引用替代lambda表达式

格式:数组类型::new

代码示例:

@Test
public void test6() {
    //lambda表达式写法 形参表示数组的长度
    Function<Integer, int[]> func1 = length -> new int[length];
    int[] array = func1.apply(10);
    System.out.println(array.length);
    //使用方法引用
    Function<Integer, int[]> func2 = int[]::new;
    System.out.println(func2.apply(10).length);

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值