jdk8以前,尤爲是在寫GUI程序的事件監聽的時候,各類的匿名內部類,大把大把拖沓的代碼,程序毫無美感可言!既然java中一切皆爲對象,那 麼,就相似於某些動態語言同樣,函數也能夠當成是對象啊!代碼塊也能夠當成是對象啊!隨着函數式編程的概念愈來愈深刻人心,java中 CODE=OBJECT的這一天終於到來了!若是你認爲lambda表達式僅僅是爲了從語法上簡化匿名內部類,那就過小看jdk8的lambda了! html
下面咱們就來看下lambda表達式是如何亮瞎你的眼的! java
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行!
這他媽的得省多少代碼啊!
有木有很興奮啊!
下面還有更刺激的!
lambda是如何作到的呢?看一下反編譯以後的字節碼:
- 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開始落地。
能夠看出來lambda並非語法糖,它不是像匿名內部類那樣生成那種帶有$的匿名類。
簡單的說,這裏只是定義了一個方法調用點,具體調用那個方法要到運行時才能決定,這就是前面所說的:延遲執行。
具體的細節請google:invokedynamic。
爲了配合lambda,jdk8引入了一個新的定義叫作:函數式接口(Functional interfaces)
函數式接口:
it is an interface that requires exactly one method to be implemented in order to satisfy the requirements of the interface.
(1)是一個接口
(2)只有一個待實現的方法
由於jdk8開始,接口能夠有default方法,因此,函數式接口也是能夠有default方法的,可是,只能有一個未實現的方法。
與此對應,新引入了一個註解: @FunctionalInterface
這個註解只是起文檔的做用,說明這個接口是函數式接口,編譯器並不會使用這個註解來決定一個接口是否是函數式接口。
無論加不加@FunctionalInterface這個註解,下面的接口都是函數式接口:
interface Something {
public String doit(Integer i);
}
lambda的語法
A lambda in Java essentially consists of three parts: a parenthesized set of parameters, an arrow, and then a body,
which can either be a single expression or a block of Java code.
lambda包含3個部分:
(1)括弧包起來的參數
(2)一個箭頭
(3)方法體,能夠是單個語句,也能夠是語句塊
參數能夠寫類型,也能夠不寫,jvm很智能的,它能本身推算出來
- 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);
- }
- }
一個頗有意思的事情:
以前咱們說Object是一切類的父類,然而在加入了lambda之後,這種大一統的局面將不復存在:
- public class Test5{
- public static void main(String args[]){
- Object r = ()->System.out.println("hello,lambda");
- }
-
- }
編譯報錯:
Test5.java:3: error: incompatible types: Object is not a functional interface
Object r = ()->System.out.println("hello,lambda");
^
1 error
很顯然,編譯器會檢查變量的引用類型裏面是否真的是一個函數式接口。那麼如何讓這段代碼經過編譯呢?
只須要加一個強制類型轉換就能夠了: express
- 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類。
想要引用Hello類須要Hello.this這樣:
- class Hello {
- public Runnable r = new Runnable() {
- public void run() {
- System.out.println(Hello.this);
- System.out.println(Hello.this.toString());
- }
- };
- }
這種作法很是的反人類反直覺!看上去很噁心!
下面咱們就來看一下偉大的lambda是什麼樣子的:
- 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();
- }
- }
輸出:
Hello's custom toString()
Hello's custom toString()
System.out.println(this);這裏的this指的是Hello,而非lambda表達式!
可是,若是咱們想在lambda表達式中返回lambda自己該怎麼作呢?
變量捕獲
匿名內部類只能引用做用域外面的final的變量,在lambda中對這個限制作了削弱,只須要是「等價final」就能夠,不必用final關鍵字來標識。
- 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
Runnable r = () -> System.out.println(message);
^
1 error
若是上面的內容看上去平淡無奇的話,真正的大殺器出現了:方法引用
咱們有一個這樣的類: json
- 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這種調用方式,
若是是static方法使用:類名::方法名
若是是instance方法:instance::方法名
可是,上面的代碼仍是不是很美觀,由於Person只是一個數據對象,它不該該的對外提供compareFirstName或者是compareLastName這樣的方法,
咱們須要的僅僅是根據某個字段排序而已!很幸運的是jdk的api幫咱們作了這件事: 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方法,
可是注意這裏的Person::getFirstName,很顯然getFirstName()並非static的,這是jdk作了封裝的緣故!
這樣作就很是完美了!
假如咱們的排序算法改成:先按照lastName,而後按照age排序呢?
- 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):簡直帥呆了!
還有更多的諸如:andThen()這樣的方法,能夠查api。
虛方法擴展
由於接口能夠有default方法,因此不少類庫都重寫了,加入了一些default的方法,好比:
interface Iterator<T> {
boolean hasNext();
T next();
void remove();
void skip(int i) default {
for (; i > 0 && hasNext(); i--) next();
}
}
skip(i)就是一個default方法,這樣全部的Iterator的子類都具備了一個叫skip的方法!
可是,你們對default方法的爭議仍是比較大的,好比:
- 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會怎樣呢?這不就是傳說中的多繼承麼?想知道結果的話,本身試一下就能夠了,哈哈
Stream:
以前的文章已經有介紹,下面只據一些使用的例子:
過濾age>12的元素:
- 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);
- }
- }
輸出結果:
[{firstName:"Ted",lastName:"Neward",age:10},{firstName:"Charlotte",lastName:"Neward",age:41},{firstName:"Michael",lastName:"Naward",age:19},{firstName:"Matthew",lastName:"Nmward",age:13}]
還能夠這樣:
- 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!
參考: http://www.oracle.com/technetwork/articles/java/architect-lambdas-part1-2080972.html http://www.oracle.com/technetwork/articles/java/architect-lambdas-part2-2081439.html