原文地址:http://www.oracle.com/technetwork/articles/java/architect-lambdas-part1-2080972.html
lambda表达式。lambda表达式只是一种较短的方法来编写用于以后执行的方法的实现。当定义一个Runnale如清单1所示,它使用匿名内部类的语法和显然患有“垂直问题”(代码需要更多的行来表达基本概念),而lambda语法允许代码如清单2所示。
List1
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello,world");
}
};
r1.run();
Runnable r2 = () -> {System.out.println("Hello,world");};
r2.run();
两者有相同的效果。但是,lambda表达式所做的不仅仅是生成一个实现Runnable接口的匿名类,其中一些与Java 7中引入的调用动态字节码有关。
函数式接口。抽象方法只能有一个,这样当lambda正在尝试定义接口的方法时,没有歧义。 @FunctionalInterface注解限定了接口只能声明一个抽象方法,但事实上编译器只要求有函数式接口的结构。
语法。lambda表达式包含三部分:一个参数的集合,一个箭头,一个主体,主体可以是一个表达式或Java代码块。在清单1中不使用参数并且返回void。对于Comparator<T>接口,使用2个参数并且返回一个整形,如清单3
List3
Comparator<String> c = (s1,s2) -> s1.compareTo(s2);
int result = c.compare("hello", "world");
List4
Comparator<String> c = (s1,s2) -> {
System.out.println("I am comparing " +s1 + " to " + s2);
return s1.compareTo(s2);
};
int result = c.compare("hello", "world");
对于清单5代码,编译器会报错,因为它不知道lambda应该实现哪个函数式接口:Runnable或其他。
List5
Object o = () -> System.out.println("hello,world");//wrong
可以通过强转使编译器认识,如清单6
List6
Object o = (Runnable)() -> System.out.println("hello,world");//right
清单7的两条语句效果是一样的
List7
Runnable r = () -> {
System.out.println(this);
System.out.println(toString());
};
因为
this不再指向lambda体。
对于变量捕获,lambda相对于内部类中使用的变量必须为final,放松了限制,变量是“ effectively final”即可,意味着该变量所有的都是final除了变量名。如清单8,因为message在范围内没有被修改,所以它实际上是final的。
List8
String message = "hello,world";
Runnable r = () -> {System.out.println(message);};
注意,lambda的语义规则不改变Java作为一个整体的性质---对象仍然可以访问和修改在lambda定义后,如清单9
List9
StringBuilder message = new StringBuilder();
Runnable r = () -> System.out.println(message);
message.append("hello");
message.append("world");
r.run();//打印结果为helloworld
这也适用于在内部类中被引用的“最终”引用,最终引用只应用于引用,而不应用于引用的另一端的对象,就如清单9的StringBuilder。
方法引用。到目前为止,所有的lambda都是匿名的,本质上是在使用的时候定义lambda,但这是一次性的行为,当这种行为在多个地方需要使用时,它将没有多大作用。考虑如下的Person类。
class Person{
public String firstName;
public String lastName;
public int age;
}
当Person对象用firstName进行排序要被多次使用时,匿名的lambda违反了DRY原则。Comparator可以作为Person的一个成员进行使用,如清单10。Comparator<T>能
被其他静态方法引用,如清单11。这种风格允许以多种方式组合功能。
List10
class Person{
public String firstName;
public String lastName;
public int age;
public final static Comparator<Person> compareFirstName = (p1,p2) -> p1.firstName.compareTo(p2.firstName);
public final static Comparator<Person> compareLastName = (p1,p2) -> p1.lastName.compareTo(p2.lastName);
public String toString() {
return "Person [firstName=" + firstName + ", lastName=" + lastName + ", age=" + age + "]";
}
public Person(String f,String l,int a){
firstName = f; lastName = l; age = a;
}
}
List11
public static void main(String[] args) {
Person[] persons = new Person[] {
new Person("Ted", "Neward", 41),
new Person("Charlotte", "Neward", 41),
new Person("Michael", "Neward", 19),
new Person("Matthew", "Neward", 13)
};
Arrays.sort(persons, Person.compareFirstName);
for (Person person : persons) {
System.out.println(person);
}
}
但是,什么是方法引用允许的呢(清单12)?注意双冒号方法命名方式,该方法是在Person上定义的方法,而不是字面上的方法。
class Person {
public String firstName;
public String lastName;
public int age;
public static int compareFirstNames(Person p1, Person p2) {
return p1.firstName.compareTo(p2.firstName);
}
// ...
}
public static void main(String... args) {
Person[] people = . . .;
// Sort by first name
Arrays.sort(people, Person::compareFirstNames);
for (Person p : people)
System.out.println(p);
}
更加简洁的,如下代码可以更好的实现复用
Arrays.sort(people, comparing(Person::getFirstName));