【Java】lambda表达式和方法引用详解(jdk1.8新特性)

1、lambda表达式(jdk1.8)

lambda表达式是用来简化函数式接口的匿名内部类的写法的。

在Java编程语言中,匿名内部类是一个特殊的类,它没有明确的类名,通常用于简化代码和实现接口。匿名内部类的定义位于一个类的局部位置,例如方法中或代码块中。
函数式接口在Java中是指仅有一个抽象方法的接口。这种接口可以用作函数式参数、返回值或者赋值给函数式变量。大部分函数式接口上可能都会有一个@FunctionalInterface的注解。

lambda表达式写法如下:

函数式接口 名称=(参数)->{
	//函数式接口唯一方法的实现
}

当匿名内部类的引用传递给接口类型或者抽象类类型,如果发现类型是函数式接口,那很明显匿名内部类重写的方法必定是函数式接口的唯一方法,因此,匿名内部类的写法可以得到简化。
由此也可以看出,并不是所有匿名内部类都能使用lambda表达式。只有函数式接口才能使用lambda表达式。

一个将函数式接口的匿名内部类化为lambda表达式的完整例子:

//匿名内部类
Comparator<Integer> comparator = new Comparator<Integer>() {
    @Override
    public int compare(Integer num1, Integer num2) {
        // 比较两个整数的大小
        return num1 - num2;
    }
};
//函数式接口
Comparator<Integer> comparator = (num1, num2) -> {return num1 - num2;};

lambda表达式在某些情况下可以略写。以下是lambda表达式的省略规则。

  • 参数类型可以省略不写
  • 如果只有一个参数,参数的括号可以省略不写
  • 如果lambda表达式中的方法体代码中只有一行代码,可以省略方法体的大括号不写(同时要省略这行代码的分号),此时如果这行代码是return语句,也必须要把return语句省略掉。

2、方法引用(jdk1.8)

lambda表达式是对函数式接口的匿名内部类的简化,那lambda表达式之中创建的对象、调用的方法是否有机会进行简化呢?这就是方法引用。

方法引用的目的是为了进一步简写lambda表达式。它的标志性符号是::

方法引用可以分为静态方法引用、实例方法引用、特定类型的方法引用以及构造器引用。

2.1、静态方法引用

如果某个lambda表达式中,只有一个类调用了它的静态方法,并且传入静态方法的参数与lambda表达式的参数类型一致,那么可以使用方法引用进行简化。

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(5);
        list.add(10);
        list.add(1);
        //lambda表达式中只调用了CompareData的一个静态方法,可用静态引用进行简写
        Collections.sort(list,CompareData::compare);
        System.out.println(list);

    }
}

class CompareData{
    public static int compare(int o1,int o2){
        return o1-o2;
    }
}

2.2、实例方法引用

如果一个静态方法中只是调用一个实例方法,并且传入实例方法的参数与lambda表达式类型一致,就可以使用实例方法引用。

与上边的静态引用不同,如果想要对实例方法进行引用,需要先创建一个对象。

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(5);
        list.add(10);
        list.add(1);
        CompareData compareData = new CompareData();
        Collections.sort(list,compareData::compareDesc);
        System.out.println(list);
    }
}

class CompareData{

    public int compareDesc(int o1,int o2){
        return o2-o1;
    }
}

2.3、特定类型方法引用

这种比较特殊。假设lambda表达式有两个参数o1、o2,如果表达式中,o1调用了一个方法,o2作为该方法的入参,那么可以简写。

如何简写?o1的类进行方法引用。

public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c");
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //无视大小写进行比较
                return o1.compareToIgnoreCase(o2);
            }
        });
        //使用lambda表达式,可以简写为:
        Collections.sort(list, (o1, o2) -> o1.compareToIgnoreCase(o2));
        //o1调用方法,o2入参,可以继续简写为:
        Collections.sort(list, String::compareToIgnoreCase);
    }
}

2.4、构造器引用

如果某个lambda表达式只是在创建对象,并且lambda表达式与构造方法参数情况一致,就可以使用构造器引用。

public class Main {
    public static void main(String[] args) {
        CreateCar cc1 = new CreateCar() {
            @Override
            public Car create(String name, int price) {
                return new Car(name, price);
            }
        };
        //函数式接口,可以用lambda表达式简化
        CreateCar cc2 = (name, price) -> new Car(name, price);
        //lambda表达式中只进行了new的操作,还可以使用方法引用进行简化
        CreateCar cc3 = Car::new;
    }
}