Java教程之Java8.0新特性之Lambda表达式:Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级。本篇文章,主要给大家介绍的是lambda表达式。 Lambda表达式Lambda表达式(也称为闭包)是Java 8中最大和最令人期待的语言改变。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理:函数式开发者非常熟悉这些概念。 很多JVM平台上的语言(Groovy、Scala等)从诞生之日就支持Lambda表达式,但是Java开发者没有选择,只能使用匿名内部类代替Lambda表达式。 Lambda的设计耗费了很多时间和很大的社区力量,最终找到一种折中的实现方案,可以实现简洁而紧凑的语言结构。而lambda表达式的使用需要和函数式接口结合。 1.函数式接口1.1.概念 函数式接口在Java中是指:有且仅有一个抽象方法的接口。 函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可 以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。 备注:“语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实 底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部 类的“语法糖”,但是二者在原理上是不同的。 1.2,格式 1. 修饰符 interface 接口名称 { 2. public abstract 返回值类型 方法名称(可选参数信息); 3. } 1.3 @FunctionalInterface注解 与 @Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解: @FunctionalInterface 。该注解可用于一个接口的定义上: 1. @FunctionalInterface 2. public interface MyFunctionalInterface { 3. void myMethod(); 4. } 一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注 意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样. 2.函数式接口的使用2.1函数式接口作为参数,方法不带参数 1. //定义函数式接口 2. public interface MyInterface{ 3. 4. public abstract void show(); 5. 6. } 7. 8. //使用(匿名内部类对象/函数式) 9. public class Demo01 { 10. 11. public static void main(String[] args) { 12. method01(new MyInterface01() { 13. 14. @Override 15. public void show() { 16. System.out.println("你好,函数式接口"); 17. } 18. }); 19. // 函数式 20. method01(() -> { 21. System.out.println("你好,函数式接口"); 22. }); 23. // 函数式简写(如果方法体中只有一句代码) 24. method01(() -> System.out.println("你好,函数式接口")); 25. } 26. 27. public static void method01(MyInterface01 inter) { 28. inter.show(); 29. } 30. 31. } 函数式接口的优势 2.2, 函数式接口作为参数,方法带参数 1. //定义函数式接口 2. public interface MyInterface02 { 3. 4. public abstract void show(String msg1, String msg2); 5. 6. } 7. 8. //使用函数式接口 9. public static void main(String[] args) { 10. //匿名内部类对象 11. method01(new MyInterface02() { 12. 13. @Override 14. public void show(String msg1, String msg2) { 15. System.out.println(msg1 + msg2); 16. } 17. }); 18. //函数式完整 19. method01((String msg1, String msg2) -> { 20. System.out.println(msg1 + msg2); 21. }); 22. //函数式简写 23. method01((msg1, msg2) -> System.out.println(msg1 + msg2)); 24. 25. } 26. 27. public static void method01(MyInterface02 inter) { 28. inter.show("hello", "函数式"); 29. } 2.3, 函数式接口作为返回值,方法不带参数 1. //定义函数式接口 2. public interface MyInterface02 { 3. 4. public abstract void show(String msg1, String msg2); 5. 6. } 7. public static void main(String[] args) { 8. 9. getInter1().show("你好", "函数式"); 10. getInter2().show("你好", "函数式"); 11. 12. } 13. 14. // 函数式完整 15. public static MyInterface02 getInter1() { 16. 17. return (String msg1, String msg2) -> { 18. System.out.println(msg1 + msg2); 19. }; 20. } 21. 22. // 函数式简写 23. public static MyInterface02 getInter2() { 24. 25. return (msg1, msg2) -> System.out.println(msg1 + msg2); 26. } 3.函数式编程应用场景3.1,概念 在兼顾面向对象特性的基础上,Java语言通过Lambda表达式使用函数式接口,就叫做函数式编程 3.2, 使用lambada作为参数 如果抛开实现原理不说,Java中的Lambda表达式可以被当作是匿名内部类的替代品。如果方法的参数是一个函数 式接口类型,那么就可以使用Lambda表达式进行替代。 1. public class Demo04Runnable{ 2. private static void startThread(Runnable task){ 3. new Thread(task).start(); 4. } 5. public static void main(String[] args) { 6. startThread(()‐>System.out.println("线程执行")); 7. } 8. } 3.3, 使用函数式接口作为返回值 1. public class Demo06Comparator { 2. 3. private static Comparator<Integer> getComparator(){ 4. return (num1,num2)‐> num1 - num2; 5. } 6. 7. public static void main(String[] args) { 8. Integer[] array = {3,2,1}; 9. Arrays.sort(array, getComparator()); 10. //遍历数组 11. } 12. } 3.4, 函数式接口的方法有返回值 1. public static void main(String[] args) { 2. 3. showMsg(new MyInterface03() { 4. 5. @Override 6. public String getMsg() { 7. return "hello functional interface"; 8. } 9. }); 10. 11. // lambada表达式 12. showMsg(() -> { 13. 14. return "hello1 functional interface"; 15. }); 16. 17. // lambda表达式简写 18. showMsg(() -> "hello1 functional interface"); 19. 20. } 21. 22. public static void showMsg(MyInterface03 inter) { 23. String msg = inter.getMsg(); 24. System.out.println(msg); 25. } 4.常用函数式接口(Supplier接口)JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。 下面是简单的几个接口及使用示例。 4.1,Supplier接口 java.util.function.Supplier<T> 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据 4.2,基本使用 1. private static String getString(Supplier<String> function ){ 2. return function.get(); 3. } 4. public static void main(String[] args){ 5. String msgA="Hello"; 6. String msgB="World"; 7. System.out.println(getString(()->msgA+msgB)); 8. } 4.2,综合案例 需求:使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用 java.lang.Integer 类。 1. public static void main(String[] args) { 2. Integer max = getMax(()->{ 3. Integer[] nums = {1,2,3,4}; 4. int max2 = nums[0]; 5. for (Integer num : nums) { 6. if(max2 < num){ 7. max2 = num; 8. } 9. } 10. return max2; 11. }); 12. System.out.println(max); 13. } 14. 15. public static Integer getMax(Supplier<Integer> supplier){ 16. return supplier.get(); 17. } 5.常用函数式接口(Consumer接口)5.1,Consumer接口 java.util.function.Consumer<T> 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定 5.2,accept方法 Consumer 接口中包含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。基本使用如: 1. public static void main(String[] args) { 2. consumeString((msg)->System.out.println(msg)); 3. } 4. 5. 6. public static void consumeString(Consumer<String> consumer){ 7. consumer.accept("hello"); 8. } 5.3, andThen方法 如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作, 然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 1. default Consumer<T> andThen(Consumer<? super T> after) { 2. Objects.requireNonNull(after); 3. return (T t) -> { accept(t); after.accept(t); }; 4. } 注: java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出 NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。 需求:先打印大写HELLO,再打印小写hello 1. public static void main(String[] args) { 2. consumeString((msg) -> System.out.println(msg.toUpperCase()), 3. (msg) -> System.out.println(msg.toLowerCase())); 4. } 5. 6. public static void consumeString(Consumer<String> consumer1, Consumer<String> consumer2) { 7. consumer1.andThen(consumer2).accept("hello"); 8. } 6.常用函数式接口(Predicate接口)有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate<T> 接口 6.1, test方法 Predicate 接口中包含一个抽象方法: boolean test(T t) 。用于条件判断的场景 1. public enum SingleClass06 { 2. INSTANCE; 3. } 6.2,基本使用 1. public static void main(String[] args) { 2. System.out.println(predicateTest((msg) -> msg.length() > 3, "hello")); 3. } 4. 5. 6. public static boolean predicateTest(Predicate<String> predicate,String msg){ 7. return predicate.test(msg); 8. 9. } 7.总结在本文中,我们学会了使用lambda表达式的不同方式,同时也学习了java8.0开始自带的一些常用函数式接口。 |
|
来自: 好程序员IT > 《Java培训教程》