Lambda表达式
Lambda定义
Why
当使用匿名类的时候,如果这个匿名类十分简单(例如只有一个接口),这样代码看起来较为笨重和不简洁。在使用此类只有一个接口的匿名类的过程中,我们往往把它作为一个参数传递下去,例如button点击事件,而Lambda表达式可以有效解决此类问题。
What
Lambda 表达式的语法格式如下:
(parameters) -> expression
或
(parameters) ->{ statements; }
Lambda表达式的几个特征
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
Lambda使用
public class LambdaTester {
public static void main(String args[]){
LambdaTester tester = new LambdaTester();
// 类型声明
MathOperation addition = (int a, int b) -> a + b;
// 不用类型声明
MathOperation subtraction = (a, b) -> a - b;
// 大括号中的返回语句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有大括号及返回语句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
输出结果:
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google
再比如使用Lambda实现 Runnable接口 :
// 1.1使用匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
}).start();
// 1.2使用 lambda expression
new Thread(() -> System.out.println("Hello world !")).start();
// 2.1使用匿名内部类
Runnable race1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
};
// 2.2使用 lambda expression
Runnable race2 = () -> System.out.println("Hello world !");
// 直接调用 run 方法(没开新线程哦!)
race1.run();
race2.run();
可以看出Lambda表达式可以将代码变得十分简洁。
变量作用域
lambda 表达式只能引用标记了 final 的外层局部变量
public class LambdaTester {
final static String salutation = "Hello! ";
public static void main(String args[]){
GreetingService greetService1 = message ->
System.out.println(salutation + message);
greetService1.sayMessage("Runoob");
}
interface GreetingService {
void sayMessage(String message);
}
}
这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)
public class Java8Tester {
public static void main(String args[]) {
final int num = 1;
Converter s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2); // 输出结果为 3
}
public interface Converter {
void convert(int i);
}
}
int num = 1;
Converter s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;
//报错信息:Local variable num defined in an enclosing scope must be final or effectively
final
Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量
String first = "";
Comparator comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错
方法引用
方法引用定义
What
方法引用是一个Lambda表达式,可以直接访问类或者实例的已经存在的方法或者构造方法。
方法引用的操作符是双冒号"::"。
方法引用使用
方法引用提供了四种实现方式:
类型 | 示例 |
---|---|
引用静态方法 | ContainingClass::staticMethodName |
引用某个对象的实例方法 | containingObject::instanceMethodName |
引用特定类型的任意对象的实例方法 | ContainingType::methodName |
引用构造函数 | ClassName::new |
引用静态方法
举个栗子:
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
public int getAge() {
// ...
}
public Calendar getBirthday() {
return birthday;
}
public static int compareByAge(Person a, Person b) {
return a.birthday.compareTo(b.birthday);
}}
假设我们一个Person数组,需要进行排序,那么需要进行以下逻辑:
Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);
class PersonAgeComparator implements Comparator {
public int compare(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}
Arrays.sort(rosterAsArray, new PersonAgeComparator());
这里可以看下Arrays的sort方法:
static void sort(T[] a, Comparator super T> c)
这时,可以使用前面说到的Lambda表达式来进行转化:
Arrays.sort(rosterAsArray,
(Person a, Person b) -> {
return a.getBirthday().compareTo(b.getBirthday());
}
);
或者使用单行Lambda表示:
Arrays.sort(rosterAsArray,
(a, b) -> Person.compareByAge(a, b)
);
这里能否将code进一步简化了呢?此节中方法引用可以解决这个问题:
Arrays.sort(rosterAsArray, Person::compareByAge);
这个也是四种方法的第一种:引用静态方法。
引用某个对象的实例方法
此方法和引用静态方法类似,只不过这里使用对象引用而不是类名。这里用上面的栗子稍微改动一下:
class ComparisonProvider {
public int compareByName(Person a, Person b) {
return a.getName().compareTo(b.getName());
}
public int compareByAge(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
引用特定类型的任意对象的实例方法
以下是对特定类型的任意对象的实例方法的引用示例:
String[] stringArray = { "Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
这里的String::compareToIgnoreCase将会有(String a, String b)两个参数,方法引用会调用a.compareToIgnoreCase(b)。
引用构造函数
构造方法引用又分构造方法引用和数组构造方法引用。
构造方法引用
e.g String::new, 等价于lambda表达式 () -> new String()
数组构造方法引用
TypeName[]::new 是一个含有一个参数的构造器引用,这个参数就是数组的长度。等价于lambda表达式 x -> new TypeName[x]。举个栗子:
IntFunction arrayMaker = int[]::new;
int[] array = arrayMaker.apply(10) // 创建数组 int[10]
默认和静态接口函数
What
简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需在方法名前面加个default关键字即可实现默认方法。
How
默认方法
默认方法语法格式如下:
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}
多个接口时,如何处理呢?
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}
public interface FourWheeler {
default void print(){
System.out.println("我是一辆四轮车!");
}
}
第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:
public class Car implements Vehicle, FourWheeler {
default void print(){
System.out.println("我是一辆四轮汽车!");
}
}
第二种解决方案可以使用 super 来调用指定接口的默认方法:
public class Car implements Vehicle, FourWheeler {
public void print(){
Vehicle.super.print();
}
}
静态默认方法
Java 8 的另一个特性是接口可以声明(并且可以提供实现)静态方法。例如:
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
// 静态方法
static void blowHorn(){
System.out.println("按喇叭!!!");
}
}
那如何使用呢?接下来举个例子:
public class Java8Tester {
public static void main(String args[]){
Vehicle vehicle = new Car();
vehicle.print();
}
}
interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
static void blowHorn(){
System.out.println("按喇叭!!!");
}
}
interface FourWheeler {
default void print(){
System.out.println("我是一辆四轮车!");
}
}
class Car implements Vehicle, FourWheeler {
public void print(){
Vehicle.super.print();
FourWheeler.super.print();
Vehicle.blowHorn();
System.out.println("我是一辆汽车!");
}
}
执行以上脚本,输出结果为:
我是一辆车!
我是一辆四轮车!
按喇叭!!!
我是一辆汽车!
到这里,Android中Java8常用新特性已经讲完了,大家在开发过程中会使用新特性的频率有多少呢?
参考资料:
http://www.runoob.com/java/java8-lambda-expressions.html
http://www.cnblogs.com/franson-2016/p/5593080.html