JDK1.8新特性lambda表達式 官方文檔

JDK1.8官方文檔說明原址

匿名類的一個問題是,如果您的匿名類的實現非常簡單,例如一個接口只包含一個方法,那麼匿名類的語法可能看起來很笨拙和不清楚。在這些情況下,您通常嘗試將功能作爲參數傳遞給另一個方法,例如當有人單擊按鈕時應該採取什麼操作。Lambda表達式允許您這樣做,將功能視爲方法參數,或將代碼視爲數據。
前一節,匿名類,向您展示瞭如何實現一個基類而不給它一個名稱。儘管這通常比命名類更簡潔,但對於只有一個方法的類,即使是匿名類也顯得有些多餘和麻煩。Lambda表達式允許您更緊湊地表達單一方法類的實例。

  1. Lambda表達式的理想用例

Suppose that you are creating a social networking application. You want to create a feature that enables an administrator to perform any kind of action, such as sending a message, on members of the social networking application that satisfy certain criteria. The following table describes this use case in detail
假設您正在創建社交網絡應用程序,如果你想創建一個功能,使管理員能夠執行任何類型的行動,如發送消息,滿足社會網絡應用程序成員的一定的標準。下面的表描述了這個用例細節:

Field Description
Name名字 Perform action on selected members對選定成員執行操作
Primary Actor 主要演員 Administrator管理員
Preconditions 前提條件 Administrator is logged in to the system.管理員登錄到系統。
Postconditions 後置條件 Action is performed only on members that fit the specified criteria.僅對符合指定標準的成員執行操作。
Main Success Scenario 主成功場景 1. Administrator specifies criteria of members on which to perform a certain action.管理員指定執行某些動作的成員的標準。2.Administrator specifies an action to perform on those selected members.管理員指定要在選定成員上執行的操作。3.Administrator selects the Submit button.管理員選擇提交按鈕。4.The system finds all members that match the specified criteria.系統查找與指定標準匹配的所有成員。5.The system performs the specified action on all matching members.系統對所有匹配成員執行指定的操作。
Extensions 拓展 1a. Administrator has an option to preview those members who match the specified criteria before he or she specifies the action to be performed or before selecting the Submit button.管理員可以在指定要執行的操作或選擇提交按鈕之前預覽與指定條件匹配的成員。
Frequency of Occurrence 出現頻次 Many times during the day.一天會出現多次

Suppose that members of this social networking application are represented by the following Person class:
假設此社交網絡應用程序的成員由以下人員類表示:


public class Person {
 
    public enum Sex {
        MALE, FEMALE
    }
 
    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;
 
    public int getAge() {
        // ...
    }
 
    public void printPerson() {
        // ...
    }
}

Suppose that the members of your social networking application are stored in a List instance.
  假設你的社交網絡應用程序的成員存儲在List實例中。
  This section begins with a naive approach to this use case. It improves upon this approach with local and anonymous classes, and then finishes with an efficient and concise approach using lambda expressions. Find the code excerpts described in this section in the example RosterTest.
  這部分我們首先使用一個幼稚的方法。它改善了使用當地和匿名類的方法,然後完成一個簡潔的使用Lambda表達式的方法。我們可以在示例RosterTest中找到這部分描述的代碼片段。

Approach方法 1: Create Methods That Search for Members That Match One Characteristic 創建查找匹配一個特性的成員的方法

One simplistic approach is to create several methods; each method searches for members that match one characteristic, such as gender or age. The following method prints members that are older than a specified age:
  一種簡單化的方法是創建幾種方法,每個方法搜索符合性別、年齡等特徵的成員。以下方法打印大於指定年齡的成員:


public static void printPersonsOlderThan(List<Person> roster, int age) {
    for (Person p : roster) {
        if (p.getAge() >= age) {
            p.printPerson();
        }
    }
}

Note: A List is an ordered Collection. A collection is an object that groups multiple elements into a single unit. Collections are used to store, retrieve, manipulate, and communicate aggregate data. For more information about collections, see theCollections trail.
注意:List 是有序集合。集合是將多個元素組合成單個單元的對象。集合用於存儲、檢索、操作和通信聚合數據。有關集合的更多信息,請參見Collections trail。
  This approach can potentially make your application brittle, which is the likelihood of an application not working because of the introduction of updates (such as newer data types). Suppose that you upgrade your application and change the structure of the Person class such that it contains different member variables; perhaps the class records and measures ages with a different data type or algorithm. You would have to rewrite a lot of your API to accommodate this change. In addition, this approach is unnecessarily restrictive; what if you wanted to print members younger than a certain age, for example?
  這個方法可能會使應用程序變得脆弱,存在應用程序無法工作的可能性,原因是應用程序的更新(例如更新的數據類型)。假設您升級了應用程序並更改了類的結構,使其包含不同的成員變量;你也將不得不重寫很多API來適應這種變化。此外,這種方法包含了不必要的限制,例如:如果你想打印的成員比一定年齡的年輕?

Approach 2: Create More Generalized Search Methods 創建更廣義的搜索方法

The following method is more generic than printPersonsOlderThan; it prints members within a specified range of ages:
下面的方法比printpersonsolderthan更通用的;它將打印指定範圍內的成員的年齡:


public static void printPersonsWithinAgeRange(
    List<Person> roster, int low, int high) {
    for (Person p : roster) {
    	//查詢年輕區間在low和high之間的data
        if (low <= p.getAge() && p.getAge() < high) {
            p.printPerson();
        }
    }
}

What if you want to print members of a specified sex, or a combination of a specified gender and age range? What if you decide to change the Person class and add other attributes such as relationship status or geographical location? Although this method is more generic than printPersonsOlderThan, trying to create a separate method for each possible search query can still lead to brittle code. You can instead separate the code that specifies the criteria for which you want to search in a different class.
  如果您想打印指定性別的成員,或指定性別和年齡範圍的組合,該怎麼辦?如果您決定更改人物類並對其添加其他屬性,如關係狀態或地理位置,該怎麼辦?雖然這種方法比printpersonsolderthan更通用,努力創造一個單獨的方法來實現每一個可能的搜索查詢,然而仍然可能導致代碼比較脆弱。你可以在不同的類中指定不同搜索條件,從而將其分隔開。

Approach 3: Specify Search Criteria Code in a Local Class 在內部類中指定搜索條件代碼

The following method prints members that match search criteria that you specify:
下面的方法打印與指定的搜索條件相匹配的成員:


public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

This method checks each Person instance contained in the List parameter roster whether it satisfies the search criteria specified in the CheckPerson parameter tester by invoking the method tester.test. If the method tester.test returns a true value, then the method printPersons is invoked on the Person instance.
這個方法會通過調用方法tester.test檢查roster 中包含的每一個Person 實例是否滿足tester 指定的搜索條件。如果tester.test方法返回true,printPersons 方法則會執行Person實例。

o specify the search criteria, you implement the CheckPerson interface:
爲了指定搜索條件,你需要實現CheckPerson 接口:


interface CheckPerson {
    boolean test(Person p);
}

The following class implements the CheckPerson interface by specifying an implementation for the method test. This method filters members that are eligible for Selective Service in the United States: it returns a true value if its Person parameter is male and between the ages of 18 and 25:
  下面的這個類實現了CheckPerson 接口,並指定了test方法的具體實現。此方法篩選在美國有資格進行選擇性服務的成員:如果其參數爲男性,年齡在18歲至25歲之間,則返回真實值:

class CheckPersonEligibleForSelectiveService implements CheckPerson {
    public boolean test(Person p) {
        return p.gender == Person.Sex.MALE &&
            p.getAge() >= 18 &&
            p.getAge() <= 25;
    }
}

To use this class, you create a new instance of it and invoke the printPersons method:
使用這個類,你可以創建它的一個新的實例並調用printpersons方法:


printPersons(
    roster, new CheckPersonEligibleForSelectiveService());

Although this approach is less brittle—you don’t have to rewrite methods if you change the structure of the Person—you still have additional code: a new interface and a local class for each search you plan to perform in your application. Because CheckPersonEligibleForSelectiveService implements an interface, you can use an anonymous class instead of a local class and bypass the need to declare a new class for each search.
  儘管這種方法不那麼脆弱,如果你改變了Person的結構你不必重寫方法了,但是你還需要額外添加代碼:你計劃在你的應用程序中執行的新的接口和內部類。因爲CheckPersonEligibleForSelectiveService 實現了一個接口,你可以使用一個匿名類而不是內部類並且繞過了爲每一個搜索聲明一個新類。

Approach 4: Specify Search Criteria Code in an Anonymous Class 在匿名類中指定搜索條件代碼

One of the arguments of the following invocation of the method printPersons is an anonymous class that filters members that are eligible for Selective Service in the United States: those who are male and between the ages of 18 and 25:
  printPersons 方法中包含一個只有一個參數的匿名函數,其作用是過濾在美國有選擇服務資格的人的條件:男性以及年齡在18歲至25歲之間:


printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

This approach reduces the amount of code required because you don’t have to create a new class for each search that you want to perform. However, the syntax of anonymous classes is bulky considering that the CheckPerson interface contains only one method. In this case, you can use a lambda expression instead of an anonymous class, as described in the next section.
  此方法減少了所需代碼的數量,因爲您不必爲要執行的每個搜索創建新類。然而,匿名類的語法是考慮到checkperson接口只包含一個方法笨重。在這種情況下,你可以使用一個而不是一個匿名類的lambda表達式,如在下一節中描述。

Approach 5: Specify Search Criteria Code with a Lambda Expression使用lambda表達式指定搜索條件代碼

The CheckPerson interface is a functional interface(函數式). A functional interface is any interface that contains only one abstract method. (A functional interface may contain one or more default methods or static methods.) Because a functional interface contains only one abstract method, you can omit the name of that method when you implement it. To do this, instead of using an anonymous class expression, you use a lambda expression, which is highlighted in the following method invocation:
CheckPerson 接口是一個函數式接口。一個函數式接口是隻包含一個抽象方法的接口。(一個函數式接口中可以包含一個或多個默認方法或者靜態方法)因爲一個函數接口只包含一個抽象方法,所以當你實現它時,可以省略該方法的名稱。要做到這一點,而不是使用一個匿名類的表達,你使用lambda表達式,由以下方法調用了:

//根據性別和年齡區間查詢具體的某個人
printPersons(
    roster,
    (Person p) -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);

See Syntax of Lambda Expressions for information about how to define lambda expressions.可以查看lambda表達式的語法來了解如何定義lambda表達式。
  You can use a standard functional interface in place of the interface CheckPerson, which reduces even further the amount of code required.
  你可以在界面checkperson處使用一個標準的功能接口,從而進一步降低所需的代碼。

Approach 6: Use Standard Functional Interfaces with Lambda Expressions 使用標準的功能接口與Lambda表達式

interface CheckPerson {
    boolean test(Person p);
}

The interface Predicate is an example of a generic interface. (For more information about generics, see the Generics (Updated) lesson.) Generic types (such as generic interfaces) specify one or more type parameters within angle brackets (<>). This interface contains only one type parameter, T. When you declare or instantiate a generic type with actual type arguments, you have a parameterized type. For example, the parameterized type Predicate is the following:  
接口Predicate是一個泛型接口。(更多關於泛型的內容,參見Generics (Updated))。泛型類型(如泛型接口)是在尖括號中指定一個或多個類型參數。這個接口中只包含一個類型參數T。當你使用一個實際類型參數聲明或實例化一個類屬性,你就有一個參數化的 類型。例如,參數化類型Predicate :

interface Predicate<Person> {
    boolean test(Person t);
}

This parameterized type contains a method that has the same return type and parameters as CheckPerson.boolean test(Person p). Consequently, you can use Predicate in place of CheckPerson as the following method demonstrates:
  這種參數化的類型包含一個返回值類型以及參數與CheckPerson.boolean test(Person p)一樣的方法。因此,你可以在使用Predicate替換掉CheckPerson ,就像下面方法演示一樣:

public static void printPersonsWithPredicate(
    List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

As a result, the following method invocation is the same as when you invoked printPersons in Approach 3: Specify Search Criteria Code in a Local Class to obtain members who are eligible for Selective Service:
  因此,下面的方法調用與你調用Approach 3: Specify Search Criteria Code in a Local Class中的printPersons是一樣,用來獲得有選擇服務資格的會員:

printPersonsWithPredicate(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);

This is not the only possible place in this method to use a lambda
expression. The following approach suggests other ways to use lambda
expressions.

這並不是在該方法中唯一可能使用Lambda表達式的地方。以下方法舉例了的其它方式使用lambda表達式。

Approach 7: Use Lambda Expressions Throughout Your Application在整個應用程序中使用Lambda表達式

Reconsider the method printPersonsWithPredicate to see where else you
could use lambda expressions:

重新考慮printPersonsWithPredicate看是否還有別的地方可以使用Lambda表達式:

public static void printPersonsWithPredicate(
    List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

This method checks each Person instance contained in the List
parameter roster whether it satisfies the criteria specified in the
Predicate parameter tester. If the Person instance does satisfy the
criteria specified by tester, the method printPersron is invoked on
the Person instance.

這個方法檢查List類型參數roster 中的每個Person 實例是否滿足Predicate
類型接口tester規定的標準。如果Person 滿足tester中指定的測試標準,Person中的printPersron方法將會被執行。

Instead of invoking the method printPerson, you can specify a
different action to perform on those Person instances that satisfy the
criteria specified by tester. You can specify this action with a
lambda expression. Suppose you want a lambda expression similar to
printPerson, one that takes one argument (an object of type Person)
and returns void. Remember, to use a lambda expression, you need to
implement a functional interface. In this case, you need a functional
interface that contains an abstract method that can take one argument
of type Person and returns void. The Consumer interface contains
the method void accept(T t), which has these characteristics. The
following method replaces the invocation p.printPerson() with an
instance of Consumer that invokes the method accept:

替換printPerson方法,我們可以在Person實例中指定一個符合tester規定的標準的不同的要執行的操作。你可以用lambda表達式指定動作。加入你想要一個類似於printPerson的Lambda表達式,一個帶有一個參數(Person類型對象)並且返回值爲void。我們應當注意,在使用Lambda表達式,我們需要一個函數式接口。在這種情況下,你需要一個包含一個Person類型的參數並且返回值爲void的抽象方法。Consumer接口包含方法void
accept(T t),滿足需要的這些特徵。下面我們使用Consumer來替換p.printPerson():

public static void processPersons(
    List<Person> roster,
    Predicate<Person> tester,
    Consumer<Person> block) {
        for (Person p : roster) {
            if (tester.test(p)) {
                block.accept(p);
            }
        }
}

As a result, the following method invocation is the same as when you
invoked printPersons in Approach 3: Specify Search Criteria Code in a
Local Class to obtain members who are eligible for Selective Service.
The lambda expression used to print members is highlighted:
  因此,下面的方法執行與當你調用並執行Approach 3: Specify Search Criteria Code in a
Local Class的printPersons 一樣,獲得有選擇服務資格的會員。Lambda表達式用來打印成員:

processPersons(
     roster,
     p -> p.getGender() == Person.Sex.MALE
         && p.getAge() >= 18
         && p.getAge() <= 25,
     p -> p.printPerson()
);

What if you want to do more with your members’ profiles than
printing them out. Suppose that you want to validate the members’
profiles or retrieve their contact information? In this case, you need
a functional interface that contains an abstract method that returns a
value. The Function<T,R> interface contains the method R apply(T t).
The following method retrieves the data specified by the parameter
mapper, and then performs an action on it specified by the parameter
block:

如果你想在打印成員簡介的時候打印更多內容該怎麼辦?假設你想要驗證成員的簡介的合法性或檢索他們的聯繫方式該怎麼辦?在這種情況下,需要一個包含返回值的抽象方法的函數式接口。Function<T,R>接口包含方法R
apply(T t)。下面的方法通過參數mapper檢索數據,然後通過block指定的條件執行相應的響應:

public static void processPersonsWithFunction(
    List<Person> roster,
    Predicate<Person> tester,
    Function<Person, String> mapper,
    Consumer<String> block) {
    for (Person p : roster) {
        if (tester.test(p)) {
            String data = mapper.apply(p);
            block.accept(data);
        }
    }
}

The following method retrieves the email address from each member
contained in roster who is eligible for Selective Service and then
prints it:   下面的方法是從包含在可選服務中的成員中檢索每個成員的電子郵件地址,然後打印它:

processPersonsWithFunction(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25,
    p -> p.getEmailAddress(),
    email -> System.out.println(email)
);

A

Approach 8: Use Generics More Extensively 更廣泛地使用泛型

Reconsider the method processPersonsWithFunction. The following is a
generic version of it that accepts, as a parameter, a collection that
contains elements of any data type:

重新考慮processPersonsWithFunction方法。 下面的泛型示例,接收一個包含任何數據類型的集合作爲參數。

public static <X, Y> void processElements(
    Iterable<X> source,
    Predicate<X> tester,
    Function <X, Y> mapper,
    Consumer<Y> block) {
    for (X p : source) {
        if (tester.test(p)) {
            Y data = mapper.apply(p);
            block.accept(data);
        }
    }
}

To print the e-mail address of members who are eligible for Selective
Service, invoke the processElements method as follows:
  將打印有資格選擇服務成員的電子郵件地址,調用processelements方法如下:

processElements(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25,
    p -> p.getEmailAddress(),
    email -> System.out.println(email)
);

This method invocation performs the following actions此方法調用執行下列操作:

Obtains a source of objects from the collection source. In this
example, it obtains a source of Person objects from the collection
roster. Notice that the collection roster, which is a collection of
type List, is also an object of type Iterable. Filters objects that
match the Predicate object tester. In this example, the Predicate
object is a lambda expression that specifies which members would be
eligible for Selective Service. Maps each filtered object to a value
as specified by the Function object mapper. In this example, the
Function object is a lambda expression that returns the e-mail address
of a member. Performs an action on each mapped object as specified by
the Consumer object block. In this example, the Consumer object is a
lambda expression that prints a string, which is the e-mail address
returned by the Function object. You can replace each of these actions
with an aggregate operation.你可以使用聚合操作替換每個操作。

Approach 9: Use Aggregate Operations That Accept Lambda Expressions as Parameters

The following example uses aggregate operations to print the e-mail
addresses of those members contained in the collection roster who are
eligible for Selective Service:

下面的示例使用聚合操作打印包含可選服務的集合列表中包含的成員的電子郵件地址:

roster
    .stream()
    .filter(
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25)
    .map(p -> p.getEmailAddress())
    .forEach(email -> System.out.println(email));

The following table maps each of the operations the method
processElements performs with the corresponding aggregate operation:

processElements Action Aggregate Operation Obtain a source of
objects Stream stream() Filter objects that match a Predicate
object Stream filter(Predicate<? super T> predicate) Map objects to
another value as specified by a Function object Stream
map(Function<? super T,? extends R> mapper) Perform an action as
specified by a Consumer object void forEach(Consumer<? super T>
action)
  The operations filter, map, and forEach are aggregate operations. Aggregate operations process elements from a stream, not directly from
a collection (which is the reason why the first method invoked in this
example is stream). A stream is a sequence of elements. Unlike a
collection, it is not a data structure that stores elements. Instead,
a stream carries values from a source, such as collection, through a
pipeline. A pipeline is a sequence of stream operations, which in this
example is filter- map-forEach. In addition, aggregate operations
typically accept lambda expressions as parameters, enabling you to
customize how they behave.

For a more thorough discussion of aggregate operations, see the
Aggregate Operations lesson.

Lambda Expressions in GUI Applications   To process events in a
graphical user interface (GUI) application, such as keyboard actions,
mouse actions, and scroll actions, you typically create event
handlers, which usually involves implementing a particular interface.
Often, event handler interfaces are functional interfaces; they tend
to have only one method.

在GUI應用程序中處理事件,例如鍵盤操作,鼠標操作和滾輪操作,通常創建事件處理程序,通常關聯實現特定接口。通常事件處理接口是函數式接口;他們往往只有一個方法。
  In the JavaFX example HelloWorld.java (discussed in the previous
section Anonymous Classes), you can replace the highlighted anonymous
class with a lambda expression in this statement:

btn.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        System.out.println("Hello World!");
    }
});

The method invocation btn.setOnAction specifies what happens when you
select the button represented by the btn object. This method requires
an object of typeEventHandler. The
EventHandler interface contains only one method, void
handle(T event). This interface is a functional interface, so you
could use the following highlighted lambda expression to replace it:

btn.setOnAction(
          event -> System.out.println("Hello World!")
        );

Syntax(語法) of Lambda Expressions A lambda expression consists of the
following一個Lambda表達式包括以下內容:

A comma-separated(逗號分隔) list of formal parameters enclosed(封閉形式參數) in
parentheses(圓括號). The CheckPerson.test method contains one parameter,
p, which represents(代表) an instance(實例) of the Personclass.

Note: You can omit(省略) the data type of the parameters in a lambda
expression. In addition(此外), you can omit(省略) the parentheses(圓括號) if
there is only one parameter. For example, the following lambda
expression is also valid:

p -> p.getGender() == Person.Sex.MALE 
    && p.getAge() >= 18
    && p.getAge() <= 25
The arrow token, ->(箭頭標記)

A body, which consists of a single expression or a statement block.
This example uses the following expression由單個表達式或語句塊組成的主體。此示例使用以下表達式:

:

p.getGender() == Person.Sex.MALE 
    && p.getAge() >= 18
    && p.getAge() <= 25

If you specify a single expression, then the Java runtime evaluates
the expression and then returns its value(大意:將表達式的結果作爲返回值返回).
Alternatively(或者), you can use a return statement(聲明):

p -> {
    return p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25;
}

A return statement is not an expression; in a lambda expression, you
must enclose(把…圍起來) statements in braces ({}). However(然而), you do not
have to enclose a void method invocation in braces. For example, the
following is a valid(有效的) lambda expression:

email -> System.out.println(email)   Note that a lambda expression
looks a lot like a method declaration; you can consider lambda
expressions as anonymous methods—methods without a name.

注意,一個lambda表達式看起來像一個方法聲明;你可以考慮lambda表達式作爲匿名方法。   The following
example, Calculator, is an example of lambda expressions that take
more than one formal parameter:

下面的計算器例子中,lambda表達式接收多個形參:

public class Calculator {
  
    interface IntegerMath {
        int operation(int a, int b);   
    }
  
    public int operateBinary(int a, int b, IntegerMath op) {
        return op.operation(a, b);
    }
 
    public static void main(String... args) {
    
        Calculator myApp = new Calculator();
        IntegerMath addition = (a, b) -> a + b;
        IntegerMath subtraction = (a, b) -> a - b;
        System.out.println("40 + 2 = " +
            myApp.operateBinary(40, 2, addition));
        System.out.println("20 - 10 = " +
            myApp.operateBinary(20, 10, subtraction));    
    }
}

The method operateBinary performs a mathematical operation(執行數學運算) on
two integer operands(操作數). The operation itself is specified by an
instance of IntegerMath. The example defines two operations with
lambda expressions, addition and subtraction. The example prints the
following:

40 + 2 = 42 20 - 10 = 10 Accessing Local Variables of the Enclosing
Scope 訪問封閉範圍的局部變量   Like local and anonymous classes, lambda
expressions can capture variables; they have the same access to local
variables of the enclosing scope. However, unlike local and anonymous
classes, lambda expressions do not have anyshadowing issues (see
Shadowing for more information). Lambda expressions are lexically
scoped. This means that they do not inherit any names from a supertype
or introducea new level of scoping. Declarations in a lambda
expression are interpreted just as they are in the enclosing
environment. The following example,LambdaScopeTest, demonstrates this:
  像本地和匿名函數,lambda表達式可以捕獲變量;他們有權限訪問封閉範圍內的本地變量。然而,不同於本地和匿名類,lambda表達式沒有任何跟蹤問題
(see Shadowing for more
information)。Lambda表達式是詞法作用域。這意味着他們沒有繼承任何名字從父類型或引入一個新的水平域。在一個lambda表達式聲明解釋就像是在封閉的環境。下面的例子中,LambdaScopeTest,演示了這一點:

import java.util.function.Consumer;
 
public class LambdaScopeTest {
 
    public int x = 0;
 
    class FirstLevel {
 
        public int x = 1;
 
        void methodInFirstLevel(int x) {
            
            // The following statement causes the compiler to generate
            // the error "local variables referenced from a lambda expression
            // must be final or effectively final" in statement A:
            //
            // x = 99;
            
            Consumer<Integer> myConsumer = (y) -> 
            {
                System.out.println("x = " + x); // Statement A
                System.out.println("y = " + y);
                System.out.println("this.x = " + this.x);
                System.out.println("LambdaScopeTest.this.x = " +
                    LambdaScopeTest.this.x);
            };
 
            myConsumer.accept(x);
 
        }
    }
 
    public static void main(String... args) {
        LambdaScopeTest st = new LambdaScopeTest();
        LambdaScopeTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}
This example generates the following output:

x = 23
y = 23
this.x = 1
LambdaScopeTest.this.x = 0
If you substitute(代替) the parameter x in place of y in the declaration of the lambda expression myConsumer, then the compiler generates an error:

Consumer<Integer> myConsumer = (x) -> {
    // ...
}

The compiler generates the error “variable x is already defined in
method methodInFirstLevel(int)” because the lambda expression does not
introduce a new level of scoping. Consequently, you can directly
access fields, methods, and local variables of the enclosing scope.
For example, the lambda expression directly accesses the parameter x
of the methodmethodInFirstLevel. To access variables in the enclosing
class, use the keyword this. In this example, this.x refers to the
member variable FirstLevel.x.

However, like local and anonymous classes, a lambda expression can
only access local variables and parameters of the enclosing block that
are final or effectively final. For example, suppose that you add the
following assignment statement immediately after the
methodInFirstLevel definition statement:

void methodInFirstLevel(int x) {
    x = 99;
    // ...
}

Because of this assignment statement, the variable FirstLevel.x is not
effectively final anymore. As a result, the Java compiler generates an
error message similar to “local variables referenced from a lambda
expression must be final or effectively final” where the lambda
expression myConsumer tries to access the FirstLevel.x variable:

System.out.println("x = " + x);

Target Typing How do you determine the type of a lambda expression?
Recall the lambda expression that selected members who are male and
between the ages 18 and 25 years:

p -> p.getGender() == Person.Sex.MALE
    && p.getAge() >= 18
    && p.getAge() <= 25
> This lambda expression was used in the following two methods:
> 
> public static void printPersons(List<Person> roster, CheckPerson
> tester) in Approach 3: Specify Search Criteria Code in a Local Class
> 
> public void printPersonsWithPredicate(List<Person> roster,
> Predicate<Person> tester) in Approach 6: Use Standard Functional
> Interfaces with Lambda Expressions
> 
> When the Java runtime invokes the method printPersons, it's expecting
> a data type of CheckPerson, so the lambda expression is of this type.
> However, when the Java runtime invokes the method
> printPersonsWithPredicate, it's expecting a data type of
> Predicate<Person>, so the lambda expression is of this type. The data
> type that these methods expect is called the target type. To determine
> the type of a lambda expression, the Java compiler uses the target
> type of the context or situation in which the lambda expression was
> found. It follows that you can only use lambda expressions in
> situations in which the Java compiler can determine a target type:
> 
> Variable declarations
> 
> Assignments
> 
> Return statements
> 
> Array initializers
> 
> Method or constructor arguments
> 
> Lambda expression bodies
> 
> Conditional expressions, ?:
> 
> Cast expressions
> 
> Target Types and Method Arguments For method arguments, the Java
> compiler determines the target type with two other language features:
> overload resolution and type argument inference.

Consider the following two functional interfaces ( java.lang.Runnable and java.util.concurrent.Callable<V>):

public interface Runnable {
    void run();
}
 
public interface Callable<V> {
    V call();
}
The method Runnable.run does not return a value, whereas Callable<V>.call does.

Suppose that you have overloaded the method invoke as follows (see Defining Methods for more information about overloading methods):

void invoke(Runnable r) {
    r.run();
}
 
<T> T invoke(Callable<T> c) {
    return c.call();
}
Which method will be invoked in the following statement?

String s = invoke(() -> "done");
The method invoke(Callable<T>) will be invoked because that method returns a value; the method invoke(Runnable) does not. In this case, the type of the lambda expression () -> "done" is Callable<T>.

Serialization
You can serialize a lambda expression if its target type and its captured arguments are serializable. However, like inner classes, the serialization of lambda expressions is strongly discouraged.

```java
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.Comparator;
import java.util.function.Predicate;
import java.lang.Iterable;
import java.time.chrono.IsoChronology;
 
public class RosterTest {
 
    interface CheckPerson {
        boolean test(Person p);
    }
 
    // Approach 1: Create Methods that Search for Persons that Match One
    // Characteristic
 
    public static void printPersonsOlderThan(List<Person> roster, int age) {
        for (Person p : roster) {
            if (p.getAge() >= age) {
                p.printPerson();
            }
        }
    }
// Approach 2: Create More Generalized Search Methods
 
public static void printPersonsWithinAgeRange(
    List<Person> roster, int low, int high) {
    for (Person p : roster) {
        if (low <= p.getAge() && p.getAge() < high) {
            p.printPerson();
        }
    }
}
 

// Approach 3: Specify Search Criteria Code in a Local Class
// Approach 4: Specify Search Criteria Code in an Anonymous Class
// Approach 5: Specify Search Criteria Code with a Lambda Expression

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

Approach 6: Use Standard Functional Interfaces with Lambda Expressions對Lambda表達式使用標準函數接口

public static void printPersonsWithPredicate(
    List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

// Approach 7: Use Lambda Expressions Throughout Your Application在整個應用程序中使用Lambda表達式

   public static void processPersons(
        List<Person> roster,
        Predicate<Person> tester,
        Consumer<Person> block) {
        for (Person p : roster) {
            if (tester.test(p)) {
                block.accept(p);
            }
        }
    }

Approach 7, second example 第二個例子

public static void processPersonsWithFunction(
    List<Person> roster,
    Predicate<Person> tester,
    Function<Person, String> mapper,
    Consumer<String> block) {
    for (Person p : roster) {
        if (tester.test(p)) {
            String data = mapper.apply(p);
            block.accept(data);
        }
    }
}

Approach 8: Use Generics More Extensively更廣泛地使用泛型

public static <X, Y> void processElements(
    Iterable<X> source,
    Predicate<X> tester,
    Function<X, Y> mapper,
    Consumer<Y> block) {
        for (X p : source) {
            if (tester.test(p)) {
                Y data = mapper.apply(p);
                block.accept(data);
            }
        }
}
 
public static void main(String... args) {
 
    List<Person> roster = Person.createRoster();
 
    for (Person p : roster) {
        p.printPerson();
    }
 
      // Approach 1: Create Methods that Search for Persons that Match One
        // Characteristic
 
        System.out.println("Persons older than 20:");
        printPersonsOlderThan(roster, 20);
        System.out.println();
 
        // Approach 2: Create More Generalized Search Methods
 
        System.out.println("Persons between the ages of 14 and 30:");
        printPersonsWithinAgeRange(roster, 14, 30);
        System.out.println();
 
        // Approach 3: Specify Search Criteria Code in a Local Class
 
        System.out.println("Persons who are eligible for Selective Service:");
 
        class CheckPersonEligibleForSelectiveService implements CheckPerson {
           public boolean test(Person p) {
                return p.getGender() == Person.Sex.MALE
                    && p.getAge() >= 18
                    && p.getAge() <= 25;
            }
        }
 
        printPersons(
            roster, new CheckPersonEligibleForSelectiveService());
 
 
        System.out.println();
 
        // Approach 4: Specify Search Criteria Code in an Anonymous Class
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(anonymous class):");
 
        printPersons(
            roster,
            new CheckPerson() {
                public boolean test(Person p) {
                    return p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25;
                }
            }
        );
 
        System.out.println();
 
        // Approach 5: Specify Search Criteria Code with a Lambda Expression
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(lambda expression):");
 
        printPersons(
            roster,
            (Person p) -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25
        );
 
        System.out.println();
 
        // Approach 6: Use Standard Functional Interfaces with Lambda
        // Expressions
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(with Predicate parameter):");
 
        printPersonsWithPredicate(
            roster,
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25
        );
 
        System.out.println();
 
        // Approach 7: Use Lamba Expressions Throughout Your Application
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(with Predicate and Consumer parameters):");
 
        processPersons(
            roster,
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25,
            p -> p.printPerson()
        );
 
        System.out.println();
 
        // Approach 7, second example
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(with Predicate, Function, and Consumer parameters):");
 
        processPersonsWithFunction(
            roster,
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25,
            p -> p.getEmailAddress(),
            email -> System.out.println(email)
        );
 
        System.out.println();
 
        // Approach 8: Use Generics More Extensively
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(generic version):");
 
        processElements(
            roster,
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25,
            p -> p.getEmailAddress(),
            email -> System.out.println(email)
        );
 
        System.out.println();
 
        // Approach 9: Use Bulk Data Operations That Accept Lambda Expressions
        // as Parameters
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(with bulk data operations):");
 
        roster
            .stream()
            .filter(
                p -> p.getGender() == Person.Sex.MALE
                    && p.getAge() >= 18
                    && p.getAge() <= 25)
            .map(p -> p.getEmailAddress())
            .forEach(email -> System.out.println(email));
     }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章