赞
踩
Lambda 表达式是 JDK8 的一个新特性,可以取代接口的匿名内部类,写出更优雅的Java 代码。
// 在Java 8以前的代码中,为了实现带一个方法的接口,往往需要定义一个匿名类并复写接口方法,代码显得很臃肿 String[] oldWay = "Improving code with Lambda expressions in Java 8".split(" "); Arrays.sort(oldWay, new Comparator<String>() { @Override public int compare(String s1, String s2) { // 忽略大小写排序: return s1.toLowerCase().compareTo(s2.toLowerCase()); } }); System.out.println(String.join(", ", oldWay)); // 对于只有一个方法的接口,在Java 8中,现在可以把它视为一个函数,用lambda表示式简化如下: String[] newWay = "Improving code with Lambda expressions in Java 8".split(" "); Arrays.sort(newWay, (s1, s2) -> { return s1.toLowerCase().compareTo(s2.toLowerCase()); }); System.out.println(String.join(", ", newWay));
如果说匿名内部类实际上是局部内部类的更进一步,简化了局部内部类,那么Lambda就是匿名内部类更进一步,语法上更简洁了,代码更优雅了。
以上描述总体就说了三点:
- Lambda表达式仍然是局部内部类,是特殊的局部内部类,仍然定义在局部位置。而且局部内部类的注意事项,也一样存在。
- Lambda表达式在取代匿名内部类时,不是全部都取代,是取代接口的匿名内部类,而类的匿名内部类Lambda表达式是不能取代的。
- Lambda表达式是匿名内部类的更进一步, Lambda表达式得到的也不是一个类,而是一个对象,并且是接口的子类对象。
Lambda表达式非常重要,在整个Java的开发过程中,一般来说,只要涉及集合,它都是一定要使用的
而集合是开发中必然会用到的容器!!!
使用前提:
Lambda表达式表示创建接口的子类对象,但并不是所有的接口都能够用Lambda表达式创建子类对象
使用Lambda表达式要求,接口必须是功能接口(Functional Interface)
功能接口: 要求接口当中有且仅有一个必须强制子类实现的抽象方法!
- 功能接口在Java当中用注解"@FunctionalInterface"标记,如果该注解没有报错,那么接口就是一个功能接口
- 实际开发,如果自身有定义功能接口的需求,请不要忘记该注解标记!
扩展:
功能接口只有一个方法吗?
不是,因为Java8以后接口还有带实现的默认方法和静态方法,这两种方法都不需要子类强制实现(了解)功能接口只有一个抽象方法吗?
不是的
某些方法,在接口中定义,不需要强制子类实现
考虑Object,某个类在实现接口的同时,必须继承Object
如果接口中的抽象方法,可以用Object类当中的实现方法作为实现,那么该抽象方法就不会强制子类实现
当然实际开发中,碰到的大多数功能接口都是比较单纯的,往往就只有一个方法,而且就是强转子类实现的抽象方法
功能接口有了以后,就可以按照以下语法来创建功能接口的子类对象:
() -> {}
解释:
“()“表示功能接口中强制子类实现的抽象方法的形参列表
也就是直接照抄抽象方法的形参列表
比如抽象方法形参是:”()”,那么就直接写"()"
如果抽象方法形参是:“(int num)”,那么就直接写"(num)""->“由一个英文的横杠和大于号组成,它读作"goes to”,它是Lambda表达式的运算符
是固定的语法形式(重点)"{}“是重写功能接口中抽象方法的重写方法体,是方法体,不是类体
所以
①.在”{}"当中,定义的变量都是局部变量,而不是成员变量
②.Lambda表达式没有匿名子类的类体,所以也不能定义属于匿名子类的成员,只能重写抽象方法
③.Lambda表达式的语法当中,只能重写一个抽象方法,所以Lambda表达式要求接口是功能接口
如果按照上述语法,直接在局部位置写Lambda表达式,会编译报错,为什么呢?
原因在于单独写Lambda表达式,编译器无法确定Lambda表达式的具体类型,而Java是强类型语言,每一个变量/对象的类型都是需要确定.
于是接下来,就需要一个告诉编译器,Lambda表达式创建对象类型的过程,称之为"Lambda表达式的类型推断"
Lambda表达式的类型推断: 明确Lambda表达式创建的接口子类对象的类型,明确创建的是哪个接口的子类对象
包括以下方式:
(最简单直接的方式)用父类类型的引用接收,也就是用功能接口的引用接收,直接指明Lambda表达式的具体类型
// old way: Runnable oldRunnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ": Old Runnable"); } }; // new way: Runnable newRunnable = () -> { System.out.println(Thread.currentThread().getName() + ": New Lambda Runnable"); };
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
(Lambda表达式类型推断的特殊语法,很少用,了解即可,它类似于强制类型转换)
((功能接口的名字)Lambda表达式).调用方法;(最常见,常用的方式)
配合方法来完成Lambda表达式的类型推断
比如:
a.借助方法的形参数据类型来完成类型推断 假如方法的形参数据类型是一个功能接口时,具体传入的对象就可以是一个Lambda表达式创建的功能接口子类对象
b.借助方法的返回值类型来完成类型推断
假如方法的返回值类型是一个功能接口时,具体返回的对象就可以是一个Lambda表达式创建的功能接口子类对象
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。