Java基础之JDK1.8新特性lambda表达式详解
作者:码农飞哥
Lambda表达式
Lambda表达式允许把函数作为一个方法的参数(函数作为参数传递进方法中)。函数式接口有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为lambda表达式。
各种函数式接口
java.lang.Runnable java.util.concurrent.Callable java.security.PrivilegedAction java.util.Comparator java.io.FileFilter
JDK1.8 新增加的函数接口:
java.util.function
java.util.function 包下包含了很多类,用来支持Java的函数式编程
接口 | &描述 |
---|---|
BiConsumer<T,U> | 代表了一个接受两个输入参数的操作,并且不返回任何结果 |
BiFunction<T,U,R> | 代表了一个接受两个输入参数的方法,并且返回一个结果 |
BinaryOperaror | 代表了一个作用于两个同类型操作符的操作,并且返回了操作符同类型的结果 |
BiPredicate<T,U> | 代表了一个两个参数的boolean值方法 |
BooleanSupplier | 代表了boolean值结果的提供方 |
Consumer | 代表了接受一个输入参数并且无返回的操作 |
DoubleBinaryOperator | 代表了作用于两个double值操作符的操作,并且返回了一个double值的结果 |
DoubleConsumer | 代表了一个接受double值参数的操作,并且不返回结果 |
DoubleFunction | 代表接受一个double值参数的方法,并且有返回值 |
Comparator | 这个接口最主要的作用就是比较,其核心的方法是 compare(T o1, T o2),当 o1比o2小返回-1,当o1等于o2返回0,当o1大于o2返回1 |
Lambda的语法
(parameters) -> expression 或 (parameters) ->{ statements; }
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号
- 可选的大括号:如果主体包括了一个语句,就不需要使用大括号
- 可选的返回关键字:如果主体只有一个表达式返回值,则编译器会自动返回值,大括号需要指明表达式返回的一个数值。
Lambda 表达实例
//1. 不需要参数,返回值为5 ()->5; //2. 接收一个参数(数字类型),返回其2倍的值 x ->2*x //3.接受2个参数(数字),并返回他们的差值 (x,y)->x - y; //4.接收2个int型整数,返回他们的和 (int x,int y)->x+y; //5. 接收一个String对象,并在控制台打印,不返回任何值 (String s)->System.out.print(s);
举例说明
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; //大括号中的返回语句 MathOperation multipliaction = (int a, int b) -> { return a * b; }; //没有大括号及返回语句 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)); //不用括号 GreetingService greetingService1 = message -> System.out.println("Hello " + message); //用括号 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 表达式只能引用标记了final的外层局部变量,也就是说不能再lambda内部修改定义在域外的局部变量,否则会编译报错。 在lambda表达式中,只能引用值不会改变的变量。 这是因为如果在lambda表达式中改变变量,并发执行多个动作时就会不安全。对于目前为止我们看到的动作不会发生这种情况。
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 表达式
使用lambda表达式的重点是延迟执行。毕竟,如果想要立即执行代码,完全可以直接执行,而无需把它包装在一个lambda表达式中。之所以希望以后执行代码,这有很多原因,如:
- 在一个单独的线程中运行代码:
- 多次运行代码;
- 在算法的适当位置运行代码(例如:排序中的比较操作);
- 发生某种情况时执行代码(如,点击了一个按钮,数据到达,等等);
- 只在必要时才运行代码。
例如:假设你想要执行一个动作n次,将这个动作和重复次数传递到一个 repeat
方法: repeat(10,()->System.out.println("Hello,world"))
要接受这个lambda表达式,需要选择一个函数式接口。例如,我们可以使用 Runnable
接口:
public static void repeat(int n,Runnable action){ for(int i=0;i<n;i++){ action.run(); } }
如果要告诉动作出现在某次迭代中。
/** * 在某次迭代中执行动作 * @param n * @param action */ public static void repeat(int n, IntConsumer action) { for (int i = 0; i < n; i++) { action.accept(i); } }
变量作用域
lambda 表达式有3个部分
- 一个代码块;
- 参数;
- 自由变量的值,这里指非参数而且不在代码中定义的变量。
函数式接口
函数接口,是指内部只有一个抽象方法的接口。但是可以有多个非抽象方法的接口,函数式接口可以被隐式转换为lambda表达式。
使用实例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 是一个参数传递到Predicate接口的test方法 n 如果存在test方法返回true */ System.out.println("输出所有数据:"); //传递参数n eval(list, n -> true); // Predicate<Integer> dispatcher = n -> true; // n 是一个参数传递到Predicate接口的test方法 // 如果n%2为0,test方法返回true System.out.println("******输出所有的偶数:"); eval(list, n -> n % 2 == 0); // Predicate<Integer> predicate2=n->n>3 // n是一个参数传递到Predicate接口的test方法 // 如果n大于3 test方法返回true System.out.println("输出大于3的所有数字:"); 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+" "); } } } }
使用实例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(集合排序)
现在我们有一个集合list,集合的里的数据类型是HashMap。那么我们如何根据HashMap中的某个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(按照对象属性给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
自定义一个函数式接口
public class SimpleLambda { public static void main(String[] args) { start(()-> System.out.println("调用函数式接口")); } public static void start(MyRunnable runnable) { new Thread(runnable).start(); } //1. 只能有一个抽象方法 //2.默认方法除外 //3.可以加FunctionalInterface注解,也可以不加 public interface MyRunnable extends Runnable { default void myRun() { } } }
自定义一个函数式接口,传入参数,多种不同的代码编写特性:
public class SimpleLambda2 { public static void main(String[] args) { // 1.单行表达式,可以省略return run(name -> String.format(name)); // 2.代码块 run(name -> { String name1 = name; return "序列化" + name1; }); //3.方法引用,静态方法引用 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); } }
自定义一个函数式接口,带泛型的方法型函数
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); } }
总结
本文详细介绍了lambda表达式,lambda表达式是JDK1.8最重要的特性。基本上所有的内部类都可以用lambda表达式来表示。灵活的运用lambda表达式和函数式接口可以大大的简化的程序开发。
参考
[Java 8 函数式接口](
到此这篇关于Java基础之lambda表达式(JDK1.8新特性)的文章就介绍到这了,更多相关JDK1.8新特性 lambda表达式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!