jdk8之前,尤其是在写GUI程序的事件监听的时候,各种的匿名内部类,大把大把拖沓的代码,程序毫无美感可言!既然java中一切皆为对象,那么,就类似于某些动态语言一样,函数也可以当成是对象啊!代码块也可以当成是对象啊!随着函数式编程的概念越来越深入人心,java中CODE=OBJECT的这一天终于到来了!如果你认为lambda表达式仅仅是为了从语法上简化匿名内部类,那就太小看jdk8的lambda了!
下面我们就来看下lambda表达式是如何亮瞎你的眼的!
lambda的定义
Funda-men-tally, a lambda expression is just a shorter way of writing an implementation of a method for later execution.
(1)lambda是方法的实现
(2)lambda是延迟执行的
首先看一个用匿名内部类的例子:
public class Test1{ public static void main(String args[]){ Runnable r = new Runnable(){ public void run(){ System.out.println("hello,lambda!"); } }; r.run(); } }要换成lambda是什么样的呢?
public class Test2{ public static void main(String args[]){ Runnable r = ()->System.out.println("hello,lambda"); r.run(); } }原先要5行代码,现在换成了仅仅1行!
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=2, args_size=1 0: invokedynamic #2, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 5: astore_1 6: aload_1 7: invokeinterface #3, 1 // InterfaceMethod java/lang/Runnable.run:()V 12: return LineNumberTable: line 3: 0 line 4: 6 line 5: 12 }注意:上面有一个叫做invokedynamic的指令。invokedynamic是从jdk7开始引入的,jdk8开始落地。
public class Test3{ public static void main(String... args) { Comparator<String> c = (String lhs, String rhs) -> lhs.compareTo(rhs); int result = c.compare("Hello", "World"); System.out.println(result); } }方法可以有返回,也可以无返回,如果有多个语句,还要返回值,需要加上return
public class Test4{ public static void main(String... args) { Comparator<String> c =(lhs, rhs) ->{ System.out.println("I am comparing " +lhs + " to " + rhs); return lhs.compareTo(rhs); }; int result = c.compare("Hello", "World"); System.out.println(result); } }
public class Test5{ public static void main(String args[]){ Object r = ()->System.out.println("hello,lambda"); } }编译报错:
很显然,编译器会检查变量的引用类型里面是否真的是一个函数式接口。那么如何让这段代码通过编译呢?
只需要加一个强制类型转换就可以了:
public class Test6{ public static void main(String args[]){ Object r = (Runnable)()->System.out.println("hello,lambda"); } }lambda的词法作用域
我们知道,在匿名内部类中:
class Hello { public Runnable r = new Runnable() { public void run() { System.out.println(this); System.out.println(toString()); } }; public String toString() { return "Hello's custom toString()"; } } public class InnerClassExamples { public static void main(String... args) { Hello h = new Hello(); h.r.run(); } }System.out.println(this);这里的this指的是匿名类,而非Hello类。
class Hello { public Runnable r = new Runnable() { public void run() { System.out.println(Hello.this); System.out.println(Hello.this.toString()); } }; }这种做法非常的反人类反直觉!看上去很恶心!
class Hello{ public Runnable r = () -> { System.out.println(this); System.out.println(toString()); }; public String toString() { return "Hello's custom toString()"; } } public class Test7{ public static void main(String args[]){ Hello h = new Hello(); h.r.run(); } }输出:
public class Test8{ public static void main(String args[]){ String message = "Howdy, world!";//不需要是final的 Runnable r = () -> System.out.println(message);//这里也能访问 r.run(); } }“等效final”的意思是:事实上的final,所以,一旦赋值也是不可以改变的!比如:
public class Test9{ public static void main(String args[]){ String message = "Howdy, world!"; Runnable r = () -> System.out.println(message); r.run(); message = "change it"; } }Test9.java:4: error: local variables referenced from a lambda expression must be final or effectively final
如果上面的内容看上去平淡无奇的话,真正的大杀器出现了:方法引用
我们有一个这样的类:
class Person { public String firstName; public String lastName; public int age; }现在我们要把多个Person对象进行排序,有时候是按照firstName来排,有时候是按照lastName或者是age来排,使用lambda可以这样来做:
class Person{ public String firstName; public String lastName; public int age; public Person(String firstName, String lastName, int age){ this.firstName = firstName; this.lastName = lastName; this.age = age; } public String toString(){ return firstName+","+lastName+","+age; } } public class Test10{ public static void main(String args[]){ Person people[] = new Person[]{ new Person("Ted", "Neward", 41), new Person("Charlotte", "Neward", 41), new Person("Michael", "Neward", 19), new Person("Matthew", "Neward", 13) }; //sort by firstName Arrays.sort(people, (lhs,rhs)->lhs.firstName.compareTo(rhs.firstName)); for(Person p : people){ System.out.println(p); } } }我们可以把Comparator抽取出来,变成是Person对象的成员变量:
class Person{ public String firstName; public String lastName; public int age; public Person(String firstName, String lastName, int age){ this.firstName = firstName; this.lastName = lastName; this.age = age; } public String toString(){ return firstName+","+lastName+","+age; } public final static Comparator<Person> compareFirstName = (lhs, rhs) -> lhs.firstName.compareTo(rhs.firstName); public final static Comparator<Person> compareLastName = (lhs, rhs) -> lhs.lastName.compareTo(rhs.lastName); public final static Comparator<Person> compareAge = (lhs, rhs) -> lhs.age - rhs.age; } public class Test11{ public static void main(String args[]){ Person people[] = new Person[]{ new Person("Ted", "Neward", 41), new Person("Charlotte", "Neward", 41), new Person("Michael", "Neward", 19), new Person("Matthew", "Neward", 13) }; Arrays.sort(people, Person.compareFirstName);//这里直接引用lambda for(Person p : people){ System.out.println(p); } } }能起到同样的作用,但是语法看上去很奇怪,因为之前我们都是创建一个满足Comparator签名的方法,然后直接调用,而非定义一个变量,
class Person{ public String firstName; public String lastName; public int age; public Person(String firstName, String lastName, int age){ this.firstName = firstName; this.lastName = lastName; this.age = age; } public String toString(){ return firstName+","+lastName+","+age; } public static int compareFirstName(Person lhs, Person rhs){ return lhs.firstName.compareTo(rhs.firstName); } public static int compareLastName(Person lhs, Person rhs){ return lhs.lastName.compareTo(rhs.lastName); } } public class Test12{ public static void main(String args[]){ Person people[] = new Person[]{ new Person("Ted", "Neward", 41), new Person("Charlotte", "Neward", 41), new Person("Michael", "Neward", 19), new Person("Matthew", "Neward", 13) }; Arrays.sort(people, Person::compareFirstName); for(Person p : people){ System.out.println(p); } } }看Person::compareFirstName这种调用方式,
但是,上面的代码还是不是很美观,因为Person只是一个数据对象,它不应该的对外提供compareFirstName或者是compareLastName这样的方法,
我们需要的仅仅是根据某个字段排序而已!很幸运的是jdk的api帮我们做了这件事:
import java.util.*; class Person{ public String firstName; public String lastName; public int age; public Person(String firstName, String lastName, int age){ this.firstName = firstName; this.lastName = lastName; this.age = age; } public String getFirstName(){ return this.firstName; } public String getLastName(){ return this.lastName; } public String toString(){ return firstName+","+lastName+","+age; } } public class Test13{ public static void main(String args[]){ Person people[] = new Person[]{ new Person("Ted", "Neward", 41), new Person("Charlotte", "Neward", 41), new Person("Michael", "Neward", 19), new Person("Matthew", "Neward", 13) }; Arrays.sort(people, Comparator.comparing(Person::getFirstName)); for(Person p : people){ System.out.println(p); } } }Arrays.sort(people, Comparator.comparing(Person::getFirstName));这里调用了Comparator.comparing方法,
public class Test15{ public static void main(String args[]){ Person people[] = new Person[]{ new Person("Ted", "Neward", 10), new Person("Charlotte", "Neward", 41), new Person("Michael", "Naward", 19), new Person("Matthew", "Nmward", 13) }; Collections.sort(Arrays.asList(people), (lhs, rhs)->{ if(lhs.getLastName().equals(rhs.getLastName())){ return lhs.getAge()-rhs.getAge(); }else{ return lhs.getLastName().compareTo(rhs.getLastName()); } }); for(Person p : people){ System.out.println(p); } } }很显然,应该还有更好的实现方式:
public class Test16{ public static void main(String args[]){ Person people[] = new Person[]{ new Person("Ted", "Neward", 10), new Person("Charlotte", "Neward", 41), new Person("Michael", "Naward", 19), new Person("Matthew", "Nmward", 13) }; Collections.sort(Arrays.asList(people),Comparator.comparing(Person::getLastName).thenComparing(Person::getAge)); for(Person p : people){ System.out.println(p); } } }Comparator.comparing(Person::getLastName).thenComparing(Person::getAge):简直帅呆了!
interface I1 { public default void print(){ System.out.println("I1"); } public void hello(); } interface I2{ public default void print(){ System.out.println("I2"); } public void world(); } class Impl implements I1,I2{ public void hello(){ } public void world(){ } }如果在Impl上调用print会怎样呢?这不就是传说中的多继承么?想知道结果的话,自己试一下就可以了,哈哈
people .stream() .filter(it -> it.getAge() >= 21) ;过滤age>12的元素,并输出:
people.stream() .filter((it) -> it.getAge() >= 21) .forEach((it) -> System.out.println("Have a beer, " + it.getFirstName()));jdk预定义的Predicate:
Predicate<Person> drinkingAge = (it) -> it.getAge() >= 21; Predicate<Person> brown = (it) -> it.getLastName().equals("Brown"); people.stream() .filter(drinkingAge.and(brown)) .forEach((it) ->System.out.println("Have a beer, " + it.getFirstName()));map:
IntStream ages = people.stream() .mapToInt((it) -> it.getAge()); //sum: int sum = people.stream() .mapToInt(Person::getAge) .sum();重点说下reduce:
public class Test17{ public static void main(String args[]){ List<Integer> values = Arrays.asList(1,2,3,4,5); int sum = values.stream().reduce(0, (l,r)->l+r); System.out.println(sum); } }reduce(0, (l,r)->l+r)的工作原理是:第一个参数0作为后面lambda表达式的左操作数,然后从stream中取出一个元素作为右操作数,
class Person{ public String firstName; public String lastName; public int age; public Person(String firstName, String lastName, int age){ this.firstName = firstName; this.lastName = lastName; this.age = age; } public String getFirstName(){ return this.firstName; } public String getLastName(){ return this.lastName; } public int getAge(){ return this.age; } public String toString(){ return firstName+","+lastName+","+age; } public String toJson(){ return "{"+ "firstName:\""+firstName+"\","+ "lastName:\""+lastName+"\","+ "age:"+age + "}"; } } public class Test18{ public static void main(String args[]){ Person people[] = new Person[]{ new Person("Ted", "Neward", 10), new Person("Charlotte", "Neward", 41), new Person("Michael", "Naward", 19), new Person("Matthew", "Nmward", 13) }; String json = Arrays.asList(people).stream().map(Person::toJson).reduce("[",(l,r)->l + (l.equals("[")?"":",") + r)+"]"; System.out.println(json); } }输出结果:
public class Test19{ public static void main(String args[]){ Person people[] = new Person[]{ new Person("Ted", "Neward", 10), new Person("Charlotte", "Neward", 41), new Person("Michael", "Naward", 19), new Person("Matthew", "Nmward", 13) }; String joined = Arrays.asList(people).stream().map(Person::toJson).collect(Collectors.joining(", ")); System.out.println("[" + joined + "]"); } }
更进一步: public class Test20{ public static void main(String args[]){ Person people[] = new Person[]{ new Person("Ted", "Neward", 10), new Person("Charlotte", "Neward", 41), new Person("Michael", "Naward", 19), new Person("Matthew", "Nmward", 13) }; String json = Arrays.asList(people).stream().map(Person::toJson).collect(Collectors.joining(", ", "[", "]")); System.out.println(json); } } 如果只能用一个字来形容,那就是perfect!参考: