Lambda表达式是jdk1.8出现的一种重要的新特性,它允许将函数作为参数进行传递(函数式编程),能够极大的简洁代码。点击查看Oracle官网有关于jdk8的新特性描述。
以最常见到的使用Runnable创建线程为例。
使用匿名内部类创建:
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("do something here");
}
},"thread");
使用lambda表达式的形式创建:
Thread thread = new Thread(() -> System.out.println("do something here"), "thread");
可以说代码的数量减少很多,看起来也很简洁。
Lambda
简单的语法
参数 -> 表达式
args -> experssion
Lambda表达式特征
- 可选的类型声明:编译器可以通过上下文来推到类型
- 可选的参数括号:如果只有一个参数,可以不定义括号
- 可选的大括号:如果函数体只有一条语句,可以省略大括号
- 可选的返回关键字:如果主体只有一个表达式返回值,则编译器会自动返回值
简单举例
1、不需要参数: () -> 5
2、接受一个参数: x -> x+5
3、接收两个参数: (x,y) -> x-y
4、接收int类型参数:(int a, int b) -> x+y
5、接收参数,返回空: (String s) -> System.out.println(s);
应用场景
Lambda表达式适合应用在任何有函数式接口的地方
什么是函数式接口?
简单来说就是只有一个抽象方法的接口,从API的形式来看就是具有@FunctionalInterface
注解的接口。关于注解可以看我之前的文章点击查看。
那么这里强调函数式接口是具有一个抽象方法的接口。不是所有接口的方法都是抽象的么?事实上接口可以声明Object类中的方法,比如toString,equals方法。比如下面这种情况是可以的:
@FunctionalInterface interface Test { void say(); String toString(); }
而且,在jdk8以后,接口中可以添加default方法,这也不是一个抽象方法。你可以通过查看Comparator接口定义来分辨。
常见的函数式接口
java.util.function包下存在很多的函数式接口,适用于不同场景。
这里就不一一介绍了,下面如果在某个举例中用到再进行详细说明。
方法引用
方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象
方法的实现恰好可以使用调用另外一个方法来实现,就有可能可
以使用方法引用。
分类
- 静态方法引用:如果函数式接口的实现恰好可以通过调用一个静态方法来实现,那么就可以使用静态方法引用
格式:Class::staticMethod
- 实例方法引用:如果函数式接口的实现恰好可以通过调用一个实例的实例方法来实现,那么就可以使用实例方法引用
格式:Class::instanceMethod
- 对象方法引用:抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函数式接口的实现能由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用
格式:object::instanceMethod
- 构造方法引用:如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,那么就可以使用构造方法引用
格式:Class::new
使用
静态方法引用
class TT {
public static String fun2() {
System.out.println("tt -- fun2");
return "fun2";
}
}
下面分别是以方法引用的形式和lambda语法形式来执行。
public static void main(String[] args) {
Supplier<String> fun2 = TT::fun2;
System.out.println(fun2.get());
Supplier<String> supplier = () -> TT.fun2();
System.out.println(supplier.get());
}
这里提到了Supplier
,它是一个函数式接口。
Supplier< T > 没有参数,返回类型为T,通过get方法来执行获取返回值。
实例方法引用
class TT {
public void fun(String a) {
System.out.println("tt"+a+" -- fun");
}
}
public static void main(String[] args) {
Consumer<String> consumer = a -> new TT().fun(a);
consumer.accept("0");
Consumer<String> consumer1 = new TT()::fun;
consumer.accept("1");
}
这里用到了Consumer函数接口。
Consumer< T >接口是一个参数类型为T,没有返回值的函数接口。通过accept方法来传入参数并执行。
对象方法引用
public static void main(String[] args) {
Comparator<String> comparator = (a, b) -> a.compareToIgnoreCase(b);
int compare1 = comparator.compare("a", "b");
System.out.println(compare1);
Comparator<String> compare = String::compareToIgnoreCase;
int i = compare.compare("a", "b");
System.out.println(i);
}
Comparator接口大家应该很熟悉了,这里就不多赘述。
那么静态方法和对象方法两者的区别在哪里?
对象方法会将第一个参数作为方法的目标。只需要记住这样一个例子即可:String::compareToIgnoreCase
,
那么以Lambda形式来写的话就是
(a, b) -> a.compareToIgnoreCase(b)
构造方法引用
class Acc {
public Acc() {
System.out.println("无参构造");
}
public Acc(int a) {
System.out.println("有参构造"+a);
}
}
public static void main(String[] args) {
Supplier<Acc> s2 = Acc::new;
s2.get();
Consumer<Integer> c1 = Acc::new;
c1.accept(234);
Function<Integer,Acc> f1 = Acc::new;
Acc apply = f1.apply(234444);
}
这里提到了Function。
Function<T,R> ,其中T为参数,R为返回值。通过apply方法传入参数并返回
为什么用Lambda
lambda最重要的一点是延迟执行,毕竟如果要立即执行代码的话,完全没有必要写再lambda表达式中。上面代码中使用静态引用的方式:
Supplier<String> fun2 = TT::fun2;
这一句话其实是没有执行fun2函数的,大家可以尝试一下。
为什么延迟执行
- 在一个单独线程中运行代码。
- 需要多次运行
- 在适当的时候执行代码
- 在某种情况下执行代码(比如触发器)
比如输出n个数,使用lambda的方式可以这样写:
public static void repeat(int i, IntConsumer intConsumer) {
System.out.println("repeat");
for (int j = 0; j < i; j++) {
intConsumer.accept(j);
}
}
调用:
repeat(10, i -> System.out.println(9 - i));
例子
集合的迭代:
public static void main(String[] args) {
List<String> list = Arrays.asList("a,b,c,d".split(","));
list.forEach(System.out::println);
}
排序:
public static void main(String[] args) {
String[] array = {"a", "ff", "ab", "cd", "rwdfsd", "abc"};
//Arrays.sort(array, (a, b) -> a.length() - b.length());
Arrays.sort(array, Comparator.comparingInt(String::length));
System.out.println(Arrays.asList(array));
}
参考文章:《Java核心技术卷1》
毕业的事情基本告一段落了,对自己的校招进行了一些总结和梳理。现在在终于能够静下心来分享一些知识了,希望能够帮到大家!
如果有哪些不足和错误,欢迎大家指出~