Java8 Lambda表达式理解

原文地址: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");


传参数时编译器使用了类型推断,可以不明确指出类型。如果lambda体要求更多的表达式,则使用lambda体,如清单4

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));





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值