Lambda表達式
匿名類的一個問題是,如果匿名類的實現非常簡單,例如只包含一個方法的接口,那么匿名類的語法可能看起來不實用且不清楚,在這些情況下,你通常會嘗試將功能作為參數傳遞給另一個方法,例如當有人單擊按鈕時應采取的操作,Lambda表達式使你可以執行此操作,將功能視為方法參數,或將代碼視為數據。
上一節匿名類向你展示了如何在不給它命名的情況下實現基類,雖然這通常比命名類更簡潔,但對于只有一個方法的類,即使是匿名類也似乎有點過多和繁瑣,Lambda表達式允許你更緊湊地表達單方法類的實例。
Lambda表達式的理想用例假設你正在創建社交網絡應用程序,你希望創建一項功能,使管理員能夠對滿足特定條件的社交網絡應用程序成員執行任何類型的操作,例如發送消息,下表詳細描述了此用例:
字段 | 描述 |
---|---|
名稱 | 對選定的成員執行操作 |
主要角色 | 管理員 |
前提條件 | 管理員已登錄系統 |
后置條件 | 僅對符合指定條件的成員執行操作 |
主要成功案例 | 1. 管理員指定要執行特定操作的成員的條件 2. 管理員指定要對這些選定成員執行的操作 3. 管理員選擇Submit按鈕 4. 系統查找符合指定條件的所有成員 5. 系統對所有匹配成員執行指定的操作 |
擴展 | 管理員可以選擇在指定要執行的操作之前或選擇Submit按鈕之前預覽符合指定條件的成員 |
發生頻率 | 一天中很多次 |
假設此社交網絡應用程序的成員由以下Person類表示:
public class Person { public enum Sex { MALE, FEMALE } String name; LocalDate birthday; Sex gender; String emailAddress; public int getAge() { // ... } public void printPerson() { // ... } }
假設你的社交網絡應用程序的成員存儲在List
本節首先介紹這種用例的簡單方法,它使用局部和匿名類改進了這種方法,然后使用lambda表達式以高效和簡潔的方法完成,在示例RosterTest中找到本節中描述的代碼摘錄。
方法1:創建搜索匹配一個特征的成員的方法一種簡單的方法是創建幾種方法,每種方法都會搜索與一個特征匹配的成員,例如性別或年齡,以下方法打印超過指定年齡的成員:
public static void printPersonsOlderThan(Listroster, int age) { for (Person p : roster) { if (p.getAge() >= age) { p.printPerson(); } } }
注意:List是有序集合,集合是將多個元素組合到一個單元中的對象,集合用于存儲、檢索、操作和傳遞聚合數據,有關集合的更多信息,請參閱集合路徑。
這種方法可能會使你的應用程序變得脆弱,這是由于引入了更新(例如更新的數據類型)導致應用程序無法工作的可能性。假設你升級應用程序并更改Person類的結構,使其包含不同的成員變量,也許類使用不同的數據類型或算法記錄和測量年齡,你必須重寫大量API以適應此更改,此外,這種方法是不必要的限制;例如,如果你想要打印年齡小于某個年齡的成員,該怎么辦?
方法2:創建更多廣義搜索方法以下方法比printPersonsOlderThan更通用;它會在指定的年齡范圍內打印成員:
public static void printPersonsWithinAgeRange( Listroster, int low, int high) { for (Person p : roster) { if (low <= p.getAge() && p.getAge() < high) { p.printPerson(); } } }
如果你想要打印指定性別的成員,或指定性別和年齡范圍的組合,該怎么辦?如果你決定更改Person類并添加其他屬性(如關系狀態或地理位置),該怎么辦?雖然此方法比printPersonsOlderThan更通用,但嘗試為每個可能的搜索查詢創建多帶帶的方法仍然會導致代碼脆弱,你可以改為分離指定要在其他類中搜索的條件的代碼。
方法3:在局部類中指定搜索條件代碼以下方法打印與你指定的搜索條件匹配的成員:
public static void printPersons( Listroster, CheckPerson tester) { for (Person p : roster) { if (tester.test(p)) { p.printPerson(); } } }
此方法通過調用方法tester.test來檢查List參數名單中包含的每個Person實例是否滿足CheckPerson參數tester中指定的搜索條件,如果方法tester.test返回true值,則在Person實例上調用方法printPersons。
要指定搜索條件,請實現CheckPerson接口:
interface CheckPerson { boolean test(Person p); }
以下類通過指定方法test的實現來實現CheckPerson接口,此方法可過濾符合美國選擇性服務條件的成員:如果Person參數為男性且年齡介于18和25之間,則返回true值:
class CheckPersonEligibleForSelectiveService implements CheckPerson { public boolean test(Person p) { return p.gender == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; } }
要使用此類,你需要創建它的新實例并調用printPersons方法:
printPersons( roster, new CheckPersonEligibleForSelectiveService());
雖然這種方法不那么脆弱 — 如果更改Person的結構,則不必重寫方法 — 你仍然需要額外的代碼:你計劃在應用程序中執行的每個搜索的新接口和局部類,因為CheckPersonEligibleForSelectiveService實現了一個接口,所以你可以使用匿名類而不是局部類,并且無需為每次搜索聲明一個新類。
方法4:在匿名類中指定搜索條件代碼下面調用方法printPersons的一個參數是一個匿名類,它可過濾符合美國選擇性服務條件的成員:那些男性、年齡在18到25歲之間的人:
printPersons( roster, new CheckPerson() { public boolean test(Person p) { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; } } );
此方法減少了所需的代碼量,因為你不必為要執行的每個搜索創建新類,但是,考慮到CheckPerson接口只包含一個方法,匿名類的語法很笨重,在這種情況下,你可以使用lambda表達式而不是匿名類,如下一節中所述。
方法5:使用Lambda表達式指定搜索條件代碼CheckPerson接口是一個功能性接口,功能性接口是僅包含一個抽象方法的任何接口(功能性接口可能包含一個或多個默認方法或靜態方法),由于功能性接口僅包含一個抽象方法,因此在實現該方法時可以省略該方法的名稱。為此,不使用匿名類表達式,而是使用lambda表達式,該表達式在以下方法調用中顯示:
printPersons( roster, (Person p) -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25 );
有關如何定義lambda表達式的信息,請參見Lambda表達式的語法。
你可以使用標準功能性接口代替CheckPerson接口,從而進一步減少所需的代碼量。
方法6:將標準功能性接口與Lambda表達式一起使用重新考慮CheckPerson接口:
interface CheckPerson { boolean test(Person p); }
這是一個非常簡單的接口,它是一個功能性接口,因為它只包含一個抽象方法,此方法接受一個參數并返回一個布爾值,該方法非常簡單,在你的應用程序中定義一個方法可能不值得,因此,JDK定義了幾個標準的功能性接口,你可以在java.util.function包中找到它們。
例如,你可以使用Predicate
interface Predicate{ boolean test(T t); }
接口Predicate
interface Predicate{ boolean test(Person t); }
此參數化類型包含一個方法,該方法具有與CheckPerson.boolean test(Person p)相同的返回類型和參數,因此,你可以使用Predicate
public static void printPersonsWithPredicate( Listroster, Predicate tester) { for (Person p : roster) { if (tester.test(p)) { p.printPerson(); } } }
因此,以下方法調用與在方法3:在局部類中指定搜索條件代碼以獲取有資格獲得選擇性服務的成員中調用printperson時相同:
printPersonsWithPredicate( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25 );
這不是此方法中使用lambda表達式的唯一可能位置,以下方法提出了使用lambda表達式的其他方法。
方法7:在整個應用程序中使用Lambda表達式重新考慮printPersonsWithPredicate方法以查看可以使用lambda表達式的其他位置:
public static void printPersonsWithPredicate( Listroster, Predicate tester) { for (Person p : roster) { if (tester.test(p)) { p.printPerson(); } } }
此方法檢查List參數roster中包含的每個Person實例是否滿足Predicate參數tester中指定的條件,如果Person實例滿足tester指定的條件,則在Person實例上調用printPersron方法。
你可以指定一個不同的操作來執行那些滿足tester指定的條件的Person實例,而不是調用printPerson方法,你可以使用lambda表達式指定此操作。假設你想要一個類似于printPerson的lambda表達式,它接受一個參數(Person類型的對象)并返回void,請記住,要使用lambda表達式,你需要實現一個功能性接口。在這種情況下,你需要一個包含抽象方法的功能性接口,該方法可以接受一個Person類型的參數并返回void。Consumer
public static void processPersons( Listroster, Predicate tester, Consumer block) { for (Person p : roster) { if (tester.test(p)) { block.accept(p); } } }
因此,以下方法調用與在方法3:在局部類中指定搜索條件代碼以獲取有資格獲得選擇性服務的成員中調用printPersons時相同,用于打印成員的lambda表達式如下:
processPersons( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25, p -> p.printPerson() );
如果你想對成員的個人資料進行更多操作而不是打印出來,該怎么辦?假設你要驗證成員的個人資料或檢索他們的聯系信息?在這種情況下,你需要一個包含返回值的抽象方法的功能性接口,Function
public static void processPersonsWithFunction( Listroster, Predicate tester, Function mapper, Consumer block) { for (Person p : roster) { if (tester.test(p)) { String data = mapper.apply(p); block.accept(data); } } }
以下方法從有資格獲得選擇性服務的roster中包含的每個成員檢索電子郵件地址,然后將其打印出來:
processPersonsWithFunction( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25, p -> p.getEmailAddress(), email -> System.out.println(email) );方法8:更廣泛地使用泛型
重新考慮方法processPersonsWithFunction,以下是它的泛型版本,它接受包含任何數據類型元素的集合作為參數:
public staticvoid processElements( Iterable source, Predicate tester, Function mapper, Consumer block) { for (X p : source) { if (tester.test(p)) { Y data = mapper.apply(p); block.accept(data); } } }
要打印有資格獲得選擇性服務的成員的電子郵件地址,請按如下方式調用processElements方法:
processElements( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25, p -> p.getEmailAddress(), email -> System.out.println(email) );
此方法調用執行以下操作:
從集合source獲取對象源,在此示例中,它從集合roster中獲取Person對象的源,請注意,集合roster是List類型的集合,也是Iterable類型的對象。
過濾與Predicate對象tester匹配的對象,在此示例中,Predicate對象是一個lambda表達式,指定哪些成員有資格獲得選擇性服務。
將每個篩選對象映射到Function對象mapper指定的值,在此示例中,Function對象是一個lambda表達式,它返回成員的電子郵件地址。
對Consumer對象block指定的每個映射對象執行操作,在此示例中,Consumer對象是一個lambda表達式,用于打印字符串,該字符串是Function對象返回的電子郵件地址。
你可以使用聚合操作替換每個操作。
方法9:使用接受Lambda表達式作為參數的聚合操作以下示例使用聚合操作來打印有資格獲得選擇性服務的集合roster中包含的成員的電子郵件地址:
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));
下表將方法processElements執行的每個操作映射到相應的聚合操作:
processElements 行動 | 聚合操作 |
---|---|
獲取對象的源 | Stream |
過濾與Predicate對象匹配的對象 | Stream |
將對象映射到Function對象指定的另一個值 | |
執行Consumer對象指定的操作 | void forEach(Consumer super T> action) |
操作filter、map和forEach是聚合操作,聚合操作處理流中的元素,而不是直接來自集合(這是本例中調用的第一個方法是stream的原因)。流是一系列元素,與集合不同,它不是存儲元素的數據結構,相反,流通過管道攜帶來自源(例如集合)的值,管道是一系列流操作,在此示例中為filter-map-forEach,此外,聚合操作通常接受lambda表達式作為參數,使你可以自定義它們的行為方式。
有關聚合操作的更全面討論,請參閱聚合操作課程。
GUI應用程序中的Lambda表達式要處理圖形用戶界面(GUI)應用程序中的事件,例如鍵盤操作、鼠標操作和滾動操作,通常會創建事件處理程序,這通常涉及實現特定的接口,通常,事件處理程序接口是功能性接口;他們往往只有一種方法。
在JavaFX示例HelloWorld.java中(在上一節匿名類中討論過),你可以在此語句中用lambda表達式替換匿名類:
btn.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { System.out.println("Hello World!"); } });
方法調用btn.setOnAction指定在選擇由btn對象表示的按鈕時會發生什么,此方法需要EventHandler
btn.setOnAction( event -> System.out.println("Hello World!") );Lambda表達式的語法
lambda表達式包含以下內容:
括號中用逗號分隔的形式參數列表,CheckPerson.test方法包含一個參數p,它表示Person類的實例。
注意:你可以省略lambda表達式中參數的數據類型,此外,如果只有一個參數,則可以省略括號,例如,以下lambda表達式也是有效的:
p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
箭頭標記,->
代碼體,由單個表達式或語句塊組成,此示例使用以下表達式:
p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
如果指定單個表達式,則Java運行時將計算表達式,然后返回其值,或者,你可以使用return語句:
p -> { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; }
return語句不是表達式,在lambda表達式中,你必須將語句括在大括號({})中,但是,你不必在大括號中包含void方法調用,例如,以下是有效的lambda表達式:
email -> System.out.println(email)
請注意,lambda表達式看起來很像方法聲明,你可以將lambda表達式視為匿名方法 — 沒有名稱的方法。
以下示例Calculator是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)); } }
方法operateBinary對兩個整數操作數執行數學運算,操作本身由IntegerMath實例指定,該示例使用lambda表達式,addition和subtraction定義了兩個操作,該示例打印以下內容:
40 + 2 = 42 20 - 10 = 10訪問封閉范圍的局部變量
像局部和匿名類一樣,lambda表達式可以捕獲變量,它們對封閉范圍的局部變量具有相同的訪問權限,但是,與局部和匿名類不同,lambda表達式沒有任何遮蔽問題(有關更多信息,請參閱遮蔽),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; ConsumermyConsumer = (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); } }
此示例生成以下輸出:
x = 23 y = 23 this.x = 1 LambdaScopeTest.this.x = 0
如果在lambda表達式myConsumer的聲明中用參數x代替y,則編譯器會生成錯誤:
ConsumermyConsumer = (x) -> { // ... }
編譯器生成錯誤“variable x is already defined in method methodInFirstLevel(int)”,因為lambda表達式不會引入新的作用域級別,因此,你可以直接訪問封閉范圍的字段、方法和局部變量。例如,lambda表達式直接訪問methodInFirstLevel方法的參數x,要訪問封閉類中的變量,請使用關鍵字this,在此示例中,this.x引用成員變量FirstLevel.x。
但是,與局部和匿名類一樣,lambda表達式只能訪問final或有效final的封閉塊的局部變量和參數,例如,假設你在methodInFirstLevel定義語句之后立即添加以下賦值語句:
void methodInFirstLevel(int x) { x = 99; // ... }
由于這個賦值語句,變量FirstLevel.x不再是final,因此,Java編譯器生成類似于“local variables referenced from a lambda expression must be final or effectively final”的錯誤消息,其中lambda表達式myConsumer嘗試訪問FirstLevel.x變量:
System.out.println("x = " + x);目標類型
你如何確定lambda表達式的類型?回想一下lambda表達式,它選擇了男性和年齡在18到25歲之間的成員:
p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
這個lambda表達式用于以下兩種方法:
方法3:在局部類中指定搜索條件代碼中的public static void printPersons(List
方法6:將標準功能性接口與Lambda表達式一起使用中的public void printPersonsWithPredicate(List
當Java運行時調用方法printPersons時,它期望CheckPerson的數據類型,因此lambda表達式屬于這種類型,但是,當Java運行時調用方法printPersonsWithPredicate時,它期望數據類型為Predicate
變量聲明
賦值
Return語句
數組初始化
方法或構造函數參數
Lambda表達體
條件表達式,?:
轉換表達式
目標類型和方法參數對于方法參數,Java編譯器使用另外兩種語言功能確定目標類型:重載決策和類型參數推斷。
考慮以下兩個功能性接口(java.lang.Runnable和java.util.concurrent.Callable
public interface Runnable { void run(); } public interface Callable{ V call(); }
方法Runnable.run不返回值,而Callable
假設你已按如下方式重載方法invoke(有關重載方法的詳細信息,請參閱定義方法):
void invoke(Runnable r) { r.run(); }T invoke(Callable c) { return c.call(); }
將在以下語句中調用哪個方法?
String s = invoke(() -> "done");
將調用方法invoke(Callable
如果lambda表達式的目標類型及其捕獲的參數是可序列化的,則可以序列化它,但是,與內部類一樣,強烈建議不要對lambda表達式進行序列化。
方法引用你使用lambda表達式來創建匿名方法,但是,有時,lambda表達式只會調用現有方法,在這些情況下,通過名稱引用現有方法通常更清楚,方法引用使你可以這樣做;對于已經有名稱的方法,它們是緊湊的,易于閱讀的lambda表達式。
再次考慮Person類:
public class Person { public enum Sex { MALE, FEMALE } String name; LocalDate birthday; Sex gender; String emailAddress; public int getAge() { // ... } public Calendar getBirthday() { return birthday; } public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); }}
假設你的社交網絡應用程序的成員包含在一個數組中,并且你希望按年齡對數組進行排序,你可以使用以下代碼(在示例MethodReferencesTest中查找本節中描述的代碼摘錄):
Person[] rosterAsArray = roster.toArray(new Person[roster.size()]); class PersonAgeComparator implements Comparator{ public int compare(Person a, Person b) { return a.getBirthday().compareTo(b.getBirthday()); } } Arrays.sort(rosterAsArray, new PersonAgeComparator());
此調用排序的方法簽名如下:
staticvoid sort(T[] a, Comparator super T> c)
請注意,Comparator接口是一個功能性接口,因此,你可以使用lambda表達式而不是定義然后創建實現Comparator的類的新實例:
Arrays.sort(rosterAsArray, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); } );
但是,這種比較兩個Person實例的出生日期的方法已經存在為Person.compareByAge,你可以在lambda表達式的主體中調用此方法:
Arrays.sort(rosterAsArray, (a, b) -> Person.compareByAge(a, b) );
因為此lambda表達式調用現有方法,所以可以使用方法引用而不是lambda表達式:
Arrays.sort(rosterAsArray, Person::compareByAge);
方法引用Person::compareByAge在語義上與lambda表達式(a, b) -> Person.compareByAge(a, b)相同,每個都有以下特點:
它的形式參數列表是從Comparator
它的主體調用方法Person.compareByAge。
各種方法引用有四種方法引用:
種類 | 示例 |
---|---|
引用靜態方法 | ContainingClass::staticMethodName |
引用特定對象的實例方法 | containingObject::instanceMethodName |
引用特定類型的任意對象的實例方法 | ContainingType::methodName |
引用構造函數 | ClassName::new |
方法引用Person::compareByAge是對靜態方法的引用。
引用特定對象的實例方法以下是對特定對象的實例方法的引用示例:
class ComparisonProvider { public int compareByName(Person a, Person b) { return a.getName().compareTo(b.getName()); } public int compareByAge(Person a, Person b) { return a.getBirthday().compareTo(b.getBirthday()); } } ComparisonProvider myComparisonProvider = new ComparisonProvider(); Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
方法引用myComparisonProvider::compareByName調用方法compareByName,它是對象myComparisonProvider的一部分,JRE推斷出方法類型參數,在本例中是 (Person, Person)。
對特定類型的任意對象的實例方法的引用以下是對特定類型的任意對象的實例方法的引用示例:
String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" }; Arrays.sort(stringArray, String::compareToIgnoreCase);
方法引用String::compareToIgnoreCase的等效lambda表達式將具有形式參數列表(String a, String b),其中a和b是用于更好地描述此示例的任意名稱,方法引用將調用方法a.compareToIgnoreCase(b)。
引用構造函數你可以使用名稱new以與靜態方法相同的方式引用構造函數,以下方法將元素從一個集合復制到另一個集合:
public static, DEST extends Collection > DEST transferElements( SOURCE sourceCollection, Supplier collectionFactory) { DEST result = collectionFactory.get(); for (T t : sourceCollection) { result.add(t); } return result; }
功能性接口Supplier包含一個不帶參數且返回對象的方法get,因此,你可以使用lambda表達式調用方法transferElements,如下所示:
SetrosterSetLambda = transferElements(roster, () -> { return new HashSet<>(); });
你可以使用構造函數引用代替lambda表達式,如下所示:
SetrosterSet = transferElements(roster, HashSet::new);
Java編譯器推斷你要創建包含Person類型元素的HashSet集合,或者,你可以按如下方式指定:
Set何時使用嵌套類、局部類、匿名類和Lambda表達式rosterSet = transferElements(roster, HashSet ::new);
如嵌套類一節所述,嵌套類使你能夠對僅在一個地方使用的類進行邏輯分組,增加封裝的使用,并創建更易讀和可維護的代碼。局部類、匿名類和lambda表達式也賦予這些優點,但是,它們旨在用于更具體的情況:
局部類:如果你需要創建一個類的多個實例,訪問其構造函數或引入新的命名類型(例如,你需要稍后調用其他方法),請使用它。
匿名類:如果需要聲明字段或其他方法,請使用它。
lambda表達式:
如果要封裝希望傳遞給其他代碼的單個行為單元,請使用它,例如,如果要在集合的每個元素上執行某個操作,或者在流程完成時,或者在流程遇到錯誤時,你將使用lambda表達式。
如果你需要功能性接口的簡單實例并且不應用前述條件(例如,你不需要構造函數、命名類型、字段或其他方法),請使用它。
嵌套類:如果你的要求與局部類的要求類似,則需要使用它,你希望更廣泛地使用該類型,并且不需要訪問局部變量或方法參數。
如果需要訪問封閉實例的非公共字段和方法,請使用非靜態嵌套類(或內部類),如果不需要此訪問權限,請使用靜態嵌套類。
上一篇:匿名類 下一篇:枚舉類型文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72438.html
摘要:并發教程原子變量和原文譯者飛龍協議歡迎閱讀我的多線程編程系列教程的第三部分。如果你能夠在多線程中同時且安全地執行某個操作,而不需要關鍵字或上一章中的鎖,那么這個操作就是原子的。當多線程的更新比讀取更頻繁時,這個類通常比原子數值類性能更好。 Java 8 并發教程:原子變量和 ConcurrentMap 原文:Java 8 Concurrency Tutorial: Synchroni...
摘要:簡明教程原文譯者黃小非來源簡明教程并沒有沒落,人們很快就會發現這一點歡迎閱讀我編寫的介紹。編譯器會自動地選擇合適的構造函數來匹配函數的簽名,并選擇正確的構造函數形式。 Java 8 簡明教程 原文:Java 8 Tutorial 譯者:ImportNew.com - 黃小非 來源:Java 8簡明教程 ? Java并沒有沒落,人們很快就會發現這一點 歡迎閱讀我編寫的Java ...
聚合操作 你使用集合做什么?你不可能簡單地將對象存儲在集合中并將它們留在那里,在大多數情況下,使用集合檢索存儲在其中的項。 再次考慮Lambda表達式小節中描述的場景,假設你正在創建一個社交網絡應用程序,你希望創建一個功能,使管理員能夠對滿足某些條件的社交網絡應用程序的成員執行任何類型的操作,例如發送消息。 如前所述,假設這個社交網絡應用程序的成員由以下Person類表示: public clas...
摘要:使用表達式,使得應用變得簡潔而緊湊。很多語言等從設計之初就支持表達式。表達式的參數與函數式接口內方法的參數,返回值類型相互對應。更多教程和資料請上騰訊課堂樂字節 showImg(https://segmentfault.com/img/bVbtotg?w=935&h=345); Java8 引入Lambda表達式,允許開發者將函數當成參數傳遞給某個方法,或者把代碼本身當作數據進行處理。...
摘要:在這個示例中我們使用了一個單線程線程池的。在延遲消逝后,任務將會并發執行。這是并發系列教程的第一部分。第一部分線程和執行器第二部分同步和鎖第三部分原子操作和 Java 8 并發教程:線程和執行器 原文:Java 8 Concurrency Tutorial: Threads and Executors 譯者:BlankKelly 來源:Java8并發教程:Threads和Execut...
閱讀 1739·2021-09-26 09:46
閱讀 3017·2021-09-22 15:55
閱讀 2608·2019-08-30 14:17
閱讀 3027·2019-08-26 11:59
閱讀 1809·2019-08-26 11:35
閱讀 3155·2019-08-26 10:45
閱讀 3152·2019-08-23 18:28
閱讀 1106·2019-08-23 18:21