Java基礎(chǔ)之JDK1.8新特性lambda表達(dá)式詳解
Lambda表達(dá)式
Lambda表達(dá)式允許把函數(shù)作為一個(gè)方法的參數(shù)(函數(shù)作為參數(shù)傳遞進(jìn)方法中)。函數(shù)式接口有且僅有一個(gè)抽象方法,但是可以有多個(gè)非抽象方法的接口。函數(shù)式接口可以被隱式轉(zhuǎn)換為lambda表達(dá)式。
各種函數(shù)式接口
java.lang.Runnable java.util.concurrent.Callable java.security.PrivilegedAction java.util.Comparator java.io.FileFilter
JDK1.8 新增加的函數(shù)接口:
java.util.function
java.util.function 包下包含了很多類,用來(lái)支持Java的函數(shù)式編程
接口 | &描述 |
---|---|
BiConsumer<T,U> | 代表了一個(gè)接受兩個(gè)輸入?yún)?shù)的操作,并且不返回任何結(jié)果 |
BiFunction<T,U,R> | 代表了一個(gè)接受兩個(gè)輸入?yún)?shù)的方法,并且返回一個(gè)結(jié)果 |
BinaryOperaror | 代表了一個(gè)作用于兩個(gè)同類型操作符的操作,并且返回了操作符同類型的結(jié)果 |
BiPredicate<T,U> | 代表了一個(gè)兩個(gè)參數(shù)的boolean值方法 |
BooleanSupplier | 代表了boolean值結(jié)果的提供方 |
Consumer | 代表了接受一個(gè)輸入?yún)?shù)并且無(wú)返回的操作 |
DoubleBinaryOperator | 代表了作用于兩個(gè)double值操作符的操作,并且返回了一個(gè)double值的結(jié)果 |
DoubleConsumer | 代表了一個(gè)接受double值參數(shù)的操作,并且不返回結(jié)果 |
DoubleFunction | 代表接受一個(gè)double值參數(shù)的方法,并且有返回值 |
Comparator | 這個(gè)接口最主要的作用就是比較,其核心的方法是 compare(T o1, T o2),當(dāng) o1比o2小返回-1,當(dāng)o1等于o2返回0,當(dāng)o1大于o2返回1 |
Lambda的語(yǔ)法
(parameters) -> expression 或 (parameters) ->{ statements; }
- 可選類型聲明:不需要聲明參數(shù)類型,編譯器可以統(tǒng)一識(shí)別參數(shù)值
- 可選的參數(shù)圓括號(hào):一個(gè)參數(shù)無(wú)需定義圓括號(hào),但多個(gè)參數(shù)需要定義圓括號(hào)
- 可選的大括號(hào):如果主體包括了一個(gè)語(yǔ)句,就不需要使用大括號(hào)
- 可選的返回關(guān)鍵字:如果主體只有一個(gè)表達(dá)式返回值,則編譯器會(huì)自動(dòng)返回值,大括號(hào)需要指明表達(dá)式返回的一個(gè)數(shù)值。
Lambda 表達(dá)實(shí)例
//1. 不需要參數(shù),返回值為5 ()->5; //2. 接收一個(gè)參數(shù)(數(shù)字類型),返回其2倍的值 x ->2*x //3.接受2個(gè)參數(shù)(數(shù)字),并返回他們的差值 (x,y)->x - y; //4.接收2個(gè)int型整數(shù),返回他們的和 (int x,int y)->x+y; //5. 接收一個(gè)String對(duì)象,并在控制臺(tái)打印,不返回任何值 (String s)->System.out.print(s);
舉例說(shuō)明
public class Java8Tester { public static void main(String[] args) { Java8Tester tester = new Java8Tester(); //類型聲明 MathOperation addition = (int a, int b) -> a + b; //不用聲明類型 MathOperation subtraction = (a, b) -> a - b; //大括號(hào)中的返回語(yǔ)句 MathOperation multipliaction = (int a, int b) -> { return a * b; }; //沒(méi)有大括號(hào)及返回語(yǔ)句 MathOperation division = (int a, int b) -> a / b; System.out.println("10+5=" + tester.operate(10, 5, addition)); System.out.println("10-5=" + tester.operate(10, 5, subtraction)); System.out.println("10*5=" + tester.operate(10, 5, multipliaction)); System.out.println("10/5=" + tester.operate(10, 5, division)); //不用括號(hào) GreetingService greetingService1 = message -> System.out.println("Hello " + message); //用括號(hào) GreetingService greetingService2 = (message) -> System.out.println("Hello " + message); greetingService1.sayMessage("Runoob"); greetingService2.sayMessage("Google"); } interface MathOperation{ int operation(int a, int b); } interface GreetingService{ void sayMessage(String message); } private int operate(int a, int b, MathOperation mathOperation) { return mathOperation.operation(a, b); } }
變量作用域
lambda 表達(dá)式只能引用標(biāo)記了final的外層局部變量,也就是說(shuō)不能再lambda內(nèi)部修改定義在域外的局部變量,否則會(huì)編譯報(bào)錯(cuò)。 在lambda表達(dá)式中,只能引用值不會(huì)改變的變量。 這是因?yàn)槿绻趌ambda表達(dá)式中改變變量,并發(fā)執(zhí)行多個(gè)動(dòng)作時(shí)就會(huì)不安全。對(duì)于目前為止我們看到的動(dòng)作不會(huì)發(fā)生這種情況。
public class Java8Test2 { final static String salutation = "Hello!"; public static void main(String[] args) { GreetingService greetingService = message -> System.out.println(salutation + message); greetingService.sayMessage("Runoob"); } interface GreetingService { void sayMessage(String message); } }
處理lambda 表達(dá)式
使用lambda表達(dá)式的重點(diǎn)是延遲執(zhí)行。畢竟,如果想要立即執(zhí)行代碼,完全可以直接執(zhí)行,而無(wú)需把它包裝在一個(gè)lambda表達(dá)式中。之所以希望以后執(zhí)行代碼,這有很多原因,如:
- 在一個(gè)單獨(dú)的線程中運(yùn)行代碼:
- 多次運(yùn)行代碼;
- 在算法的適當(dāng)位置運(yùn)行代碼(例如:排序中的比較操作);
- 發(fā)生某種情況時(shí)執(zhí)行代碼(如,點(diǎn)擊了一個(gè)按鈕,數(shù)據(jù)到達(dá),等等);
- 只在必要時(shí)才運(yùn)行代碼。
例如:假設(shè)你想要執(zhí)行一個(gè)動(dòng)作n次,將這個(gè)動(dòng)作和重復(fù)次數(shù)傳遞到一個(gè) repeat
方法: repeat(10,()->System.out.println("Hello,world"))
要接受這個(gè)lambda表達(dá)式,需要選擇一個(gè)函數(shù)式接口。例如,我們可以使用 Runnable
接口:
public static void repeat(int n,Runnable action){ for(int i=0;i<n;i++){ action.run(); } }
如果要告訴動(dòng)作出現(xiàn)在某次迭代中。
/** * 在某次迭代中執(zhí)行動(dòng)作 * @param n * @param action */ public static void repeat(int n, IntConsumer action) { for (int i = 0; i < n; i++) { action.accept(i); } }
變量作用域
lambda 表達(dá)式有3個(gè)部分
- 一個(gè)代碼塊;
- 參數(shù);
- 自由變量的值,這里指非參數(shù)而且不在代碼中定義的變量。
函數(shù)式接口
函數(shù)接口,是指內(nèi)部只有一個(gè)抽象方法的接口。但是可以有多個(gè)非抽象方法的接口,函數(shù)式接口可以被隱式轉(zhuǎn)換為lambda表達(dá)式。
使用實(shí)例1
public class PredicateTest { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); /** * * Predicate<Integer> dispatcher = n -> true; n 是一個(gè)參數(shù)傳遞到Predicate接口的test方法 n 如果存在test方法返回true */ System.out.println("輸出所有數(shù)據(jù):"); //傳遞參數(shù)n eval(list, n -> true); // Predicate<Integer> dispatcher = n -> true; // n 是一個(gè)參數(shù)傳遞到Predicate接口的test方法 // 如果n%2為0,test方法返回true System.out.println("******輸出所有的偶數(shù):"); eval(list, n -> n % 2 == 0); // Predicate<Integer> predicate2=n->n>3 // n是一個(gè)參數(shù)傳遞到Predicate接口的test方法 // 如果n大于3 test方法返回true System.out.println("輸出大于3的所有數(shù)字:"); eval(list, n -> n > 3); } public static void eval(List<Integer> list, Predicate<Integer> predicate) { for (Integer n : list) { if (predicate.test(n)) { System.out.println(n+" "); } } } }
使用實(shí)例2
public class CompareTest { public static void main(String[] args) { List<String> words = Arrays.asList("apple", "banana", "pear"); compareLength("apple", "banana", (n1, n2) -> -1); compareLength("banana", "pear", (n1, n2) -> 1); compareLength("banana", "banana", (n1, n2) -> 0); words.sort(Comparator.comparingInt(String::length)); } public static void compareLength(String n1, String n2, Comparator comparator) { if (comparator.compare(n1, n2) == 0) { System.out.println("n1==n2" + n1 + " " + n2); } if (comparator.compare(n1, n2) > 0) { System.out.println("n1>n2" + n1 + " " + n2); } if (comparator.compare(n1, n2) < 0) { System.out.println("n1<n2" + n1 + " " + n2); } } }
使用示例3(集合排序)
現(xiàn)在我們有一個(gè)集合list,集合的里的數(shù)據(jù)類型是HashMap。那么我們?nèi)绾胃鶕?jù)HashMap中的某個(gè)key給list排序呢?
public class ListSortTest { public static void main(String[] args) { List<Map<String, Object>> list = new ArrayList<>(); Map<String, Object> map1 = new HashMap<>(); map1.put("id", 2); map1.put("name", "張二"); list.add(map1); Map<String, Object> map2 = new HashMap<>(); map2.put("id", 1); map2.put("name", "張一"); list.add(map2); Map<String, Object> map3 = new HashMap<>(); map3.put("id", 3); map3.put("name", "張三"); list.add(map3); //升序排列 Collections.sort(list, (o1, o2) -> { int o1Id = (int) o1.get("id"); int o2Id = (int) o2.get("id"); if (o1Id > o2Id) { return 1; } else { return -1; } }); //降序排列 Collections.sort(list, new Comparator<Map<String, Object>>() { @Override public int compare(Map<String, Object> o1, Map<String, Object> o2) { int o1Id = (int) o1.get("id"); int o2Id = (int) o2.get("id"); if (o1Id > o2Id) { return -1; } else { return 1; } } }); } }
使用示例4(按照對(duì)象屬性給list排序)
public class ListSortTest2 { public static void main(String[] args) { Student student1 = new Student(1, "張三"); Student student2 = new Student(2, "李四"); List<Student> studentList = new ArrayList<>(); studentList.add(student1); studentList.add(student2); //升序 studentList.sort((o1, o2) -> { int o1Id = o1.getId(); int o2Id = o2.getId(); if (o1Id > o2Id) { return 1; } else { return -1; } }); //降序 studentList.sort((o1, o2) -> { int o1Id = o1.getId(); int o2Id = o2.getId(); if (o1Id > o2Id) { return -1; } else { return 1; } }); }
使用示例4
自定義一個(gè)函數(shù)式接口
public class SimpleLambda { public static void main(String[] args) { start(()-> System.out.println("調(diào)用函數(shù)式接口")); } public static void start(MyRunnable runnable) { new Thread(runnable).start(); } //1. 只能有一個(gè)抽象方法 //2.默認(rèn)方法除外 //3.可以加FunctionalInterface注解,也可以不加 public interface MyRunnable extends Runnable { default void myRun() { } } }
自定義一個(gè)函數(shù)式接口,傳入?yún)?shù),多種不同的代碼編寫特性:
public class SimpleLambda2 { public static void main(String[] args) { // 1.單行表達(dá)式,可以省略return run(name -> String.format(name)); // 2.代碼塊 run(name -> { String name1 = name; return "序列化" + name1; }); //3.方法引用,靜態(tài)方法引用 run(SimpleLambda2::toFormat); //4.普通方法引用 run(new SimpleLambda2()::toFormat2); } static void run(Format format) { format.format("飛哥"); } static String toFormat(String param) { return "序列化" + param; } String toFormat2(String param) { return "序列化" + param; } public interface Format { String format(String name); } }
自定義一個(gè)函數(shù)式接口,帶泛型的方法型函數(shù)
public class SimpleLambda4 { public static void main(String[] args) { Apple apple = new Apple(1, "紅色", 12, "安徽"); Banner banner1 = doBuild(apple, apple1 -> { Banner banner = new Banner(); banner.setPrice(apple1.getPrice()); banner.setColor(apple1.getColor()); return banner; }); System.out.println(banner1); } public static Banner doBuild(Apple apple, Format format) { return format.build(apple); } @FunctionalInterface public interface Format<T extends Apple, R extends Banner> { R build(T t); } }
總結(jié)
本文詳細(xì)介紹了lambda表達(dá)式,lambda表達(dá)式是JDK1.8最重要的特性?;旧纤械膬?nèi)部類都可以用lambda表達(dá)式來(lái)表示。靈活的運(yùn)用lambda表達(dá)式和函數(shù)式接口可以大大的簡(jiǎn)化的程序開發(fā)。
參考
[Java 8 函數(shù)式接口](
到此這篇關(guān)于Java基礎(chǔ)之lambda表達(dá)式(JDK1.8新特性)的文章就介紹到這了,更多相關(guān)JDK1.8新特性 lambda表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot整合Rabbitmq之Confirm和Return機(jī)制
這篇文章主要介紹了Springboot整合Rabbitmq之Confirm和Return詳解,本篇重點(diǎn)進(jìn)行Confirm?機(jī)制和Return?機(jī)制的實(shí)現(xiàn)和說(shuō)明,通過(guò)實(shí)例代碼相結(jié)合給大家詳細(xì)介紹,對(duì)Springboot整合Rabbitmq相關(guān)知識(shí)感興趣的朋友一起看看吧2022-02-02使用dynamic datasource springboot starter實(shí)現(xiàn)多數(shù)據(jù)源及源碼分析
這篇文章主要介紹了使用dynamic-datasource-spring-boot-starter做多數(shù)據(jù)源及源碼分析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09Java Guava排序器Ordering原理及代碼實(shí)例
這篇文章主要介紹了Java Guava排序器Ordering原理及代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11Java編程實(shí)現(xiàn)快速排序及優(yōu)化代碼詳解
這篇文章主要介紹了Java編程實(shí)現(xiàn)快速排序及優(yōu)化代碼詳解,具有一定借鑒價(jià)值,需要的朋友可以了解下。2017-12-12