摘录:http://www.importnew.com/16436.htmlhtml
// Java 8以前: new Thread(new Runnable() { @Override public void run() { System.out.println("Before Java8, too much code for too little to do"); } }).start();
//Java 8方式:用() -> {}代码块替代了整个匿名类 new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
output:java
too much code, for too little to do Lambda expression rocks !!
若是你的方法不对参数进行修改、重写,只是在控制台打印点东西的话,那么能够这样写:git
() -> System.out.println("Hello Lambda Expressions");
若是你的方法接收两个参数,那么能够写成以下这样:shell
若是你的方法接收两个参数,那么能够写成以下这样:
编写事件监听:express
// Java 8以前: JButton show = new JButton("Show"); show.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Event handling without lambda expression is boring"); } });
// Java 8方式: show.addActionListener((e) -> { System.out.println("Light, Camera, Action !! Lambda expressions Rocks"); });
output:编程
too much code, for too little to do Lambda expression rocks !!
// Java 8以前: List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API"); for (String feature : features) { System.out.println(feature); }
// Java 8以后: List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API"); features.forEach(n -> System.out.println(n)); // 使用Java 8的方法引用更方便,方法引用由::双冒号操做符标示, // 看起来像C++的做用域解析运算符 features.forEach(System.out::println);
output:微信
Lambdas
Default Method
Stream API
Date and Time API
java.util.function.Predicate 函数式接口以及lambda表达式,能够向API方法添加逻辑,用更少的代码支持更多的动态行为。网络
public static void main(args[]){ List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp"); System.out.println("Languages which starts with J :"); filter(languages, (str)->str.startsWith("J")); System.out.println("Languages which ends with a "); filter(languages, (str)->str.endsWith("a")); System.out.println("Print all languages :"); filter(languages, (str)->true); System.out.println("Print no language : "); filter(languages, (str)->false); System.out.println("Print language whose length greater than 4:"); filter(languages, (str)->str.length() > 4); } public static void filter(List names, Predicate condition) { for(String name: names) { if(condition.test(name)) { System.out.println(name + " "); } } }
output:闭包
Languages which starts with J : Java Languages which ends with a Java Scala Print all languages : Java Scala C++ Haskell Lisp Print no language : Print language whose length greater than 4: Scala Haskell
better way:app
// 更好的办法 public static void filter(List names, Predicate condition) { names.stream().filter((name) -> (condition.test(name))).forEach((name) -> { System.out.println(name + " "); }); }
java.util.function.Predicate 容许将两个或更多的 Predicate 合成一个。它提供相似于逻辑操做符AND和OR的方法,名字叫作and()、or()和xor(),用于将传入 filter() 方法的条件合并起来。
// 甚至能够用and()、or()和xor()逻辑函数来合并Predicate, // 例如要找到全部以J开始,长度为四个字母的名字,你能够合并两个Predicate并传入 Predicate<String> startsWithJ = (n) -> n.startsWith("J"); Predicate<String> fourLetterLong = (n) -> n.length() == 4; names.stream() .filter(startsWithJ.and(fourLetterLong)) .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));
也可使用 or() 和 xor() 方法
将 costBeforeTax 列表的每一个元素转换成为税后的值。咱们将 x -> x*x lambda表达式传到 map() 方法,后者将其应用到流中的每个元素。而后用 forEach() 将列表元素打印出来。
// 不使用lambda表达式为每一个订单加上12%的税 List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500); for (Integer cost : costBeforeTax) { double price = cost + .12*cost; System.out.println(price); } // 使用lambda表达式 List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500); costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);
112.0 224.0 336.0 448.0 560.0 112.0 224.0 336.0 448.0 560.0
Map和Reduce操做是函数式编程的核心操做,由于其功能,reduce 又被称为折叠操做。
SQL中相似 sum()、avg() 或者 count() 的汇集函数,实际上就是 reduce 操做,由于它们接收多个值并返回一个值。
流API定义的 reduceh() 函数能够接受lambda表达式,并对全部值进行合并。IntStream这样的类有相似 average()、count()、sum() 的内建方法来作 reduce 操做,也有mapToLong()、mapToDouble() 方法来作转换。
// 为每一个订单加上12%的税 // 老方法: List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500); double total = 0; for (Integer cost : costBeforeTax) { double price = cost + .12*cost; total = total + price; } System.out.println("Total : " + total); // 新方法:能够用内建方法,也能够本身定义 List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500); double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get(); System.out.println("Total : " + bill);
output:
Total : 1680.0
Total : 1680.0
// 建立一个字符串列表,每一个字符串长度大于2 List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList()); System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);
output:
Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg]
// 将字符串换成大写并用逗号连接起来 List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada"); String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", ")); System.out.println(G7Countries);
// 用全部不一样的数字建立一个正方形列表 List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4); List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList()); System.out.printf("Original List : %s, Square Without duplicates : %s %n", numbers, distinct);
output:
Original List : [9, 10, 3, 4, 7, 3, 4], Square Without duplicates : [81, 100, 9, 16, 49]
ntStream、LongStream 和 DoubleStream 等流的类中,有个很是有用的方法叫作 summaryStatistics() 。能够返回 IntSummaryStatistics、LongSummaryStatistics 或者 DoubleSummaryStatistic s,描述流中元素的各类摘要数据。
最大值 最小值 全部元素的总和 平均值
//获取数字的个数、最小值、最大值、总和以及平均值 List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29); IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("Highest prime number in List : " + stats.getMax()); System.out.println("Lowest prime number in List : " + stats.getMin()); System.out.println("Sum of all prime numbers : " + stats.getSum()); System.out.println("Average of all prime numbers : " + stats.getAverage());
输出:
Highest prime number in List : 29 Lowest prime number in List : 2 Sum of all prime numbers : 129 Average of all prime numbers : 12.9
既然lambda表达式即将正式取代Java代码中的匿名内部类,那么有必要对两者作一个比较分析。
一个关键的不一样点就是关键字 this:
匿名类的 this 关键字指向匿名类,而lambda表达式的 this 关键字指向包围lambda表达式的类。
另外一个不一样点是两者的编译方式:
Java编译器将lambda表达式编译成类的私有方法。使用了Java 7的 invokedynamic 字节码指令来动态绑定这个方法
1)lambda表达式仅能放入以下代码:预约义使用了 @Functional 注释的函数式接口,自带一个抽象函数的方法,或者SAM(Single Abstract Method 单个抽象方法)类型。这些称为lambda表达式的目标类型,能够用做返回类型,或lambda目标代码的参数。例如,若一个方法接收Runnable、Comparable或者 Callable 接口,都有单个抽象方法,能够传入lambda表达式。相似的,若是一个方法接受声明于 java.util.function 包内的接口,例如 Predicate、Function、Consumer 或 Supplier,那么能够向其传lambda表达式。
2)lambda表达式内可使用方法引用,仅当该方法不修改lambda表达式提供的参数。本例中的lambda表达式能够换为方法引用,由于这仅是一个参数相同的简单方法调用。
list.forEach(n -> System.out.println(n)); list.forEach(System.out::println); // 使用方法引用
然而,若对参数有任何修改,则不能使用方法引用,而需键入完整地lambda表达式,以下所示:
list.forEach((String s) -> System.out.println("*" + s + "*"));
事实上,能够省略这里的lambda参数的类型声明,编译器能够从列表的类属性推测出来。
3)lambda内部可使用静态、非静态和局部变量,这称为lambda内的变量捕获。
4)Lambda表达式在Java中又称为闭包或匿名函数,因此若是有同事把它叫闭包的时候,不用惊讶。
5)Lambda方法在编译器内部被翻译成私有方法,并派发 invokedynamic 字节码指令来进行调用。可使用JDK中的 javap 工具来反编译class文件。使用 javap -p 或 javap -c -v 命令来看一看lambda表达式生成的字节码。大体应该长这样:
private static java.lang.Object lambda$0(java.lang.String);
6)lambda表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在lambda内部修改定义在域外的变量。
List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7}); int factor = 2; primes.forEach(element -> { factor++; });
Compile time error : "local variables referenced from a lambda expression must be final or effectively final"
另外,只是访问它而不做修改是能够的,以下所示:
List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7}); int factor = 2; primes.forEach(element -> { System.out.println(factor*element); })
输出:
4 6 10 14
Java 8 Lambda表达式和理解
说明:部分资料来源于网络
时间:20190704
Lambda 表达式,也可称为闭包,它是推进 Java 8 发布的最重要新特性。Lambda 容许把函数做为一个方法的参数(函数做为参数传递进方法中)。使用 Lambda 表达式可使代码变的更加简洁紧凑。
1、语法
一、lambda 表达式的语法格式以下:
(左边)输入参数->(右边)lambda主体
(parameters) -> expression;
或
(parameters) ->{ statements; }
二、如下是lambda表达式的重要特征:
(1)输入参数:
可选类型声明:不须要声明参数类型,编译器能够统一识别参数值。
可选的参数圆括号:
a、一个参数无需定义圆括号,但多个参数须要定义圆括号。
b、若是申明了参数类型,则必定须要圆括号。
(2)lambda主体:
可选的大括号:若是主体包含了一个语句,就不须要使用大括号。
可选的返回关键字:若是主体只有一个表达式返回值则编译器会自动返回值,大括号须要指定明表达式返回了一个数值。
三、按照上面的格式,lambda不一样参数的表达式写法
(1)没有参数的表达式:
() -> System.out.println("this is no parameter Lambda expression");
(2)只有一个参数的表达式:
(x) -> System.out.println("this is only one parameter Lambda expression");
或者
(X x) -> System.out.println("this is only one parameter Lambda expression");
和
(x) -> { x = x*2; System.out.println("this is only one parameter Lambda expression"); System.out.println("the function is double input value"); return x; };
说明:一个参数的能够不用使用(),若是参数声明了参数类型则必需要加();Lambda主体是语句块的话须要使用{}。
(3)有两个或者多个参数的表达式:
(x,y) -> System.out.println("these are several parameters Lambda expression");
或者
(X x,Y y) -> { System.out.println("the function is add two input values"); System.out.println("these are several parameters Lambda expression"); return x+y; };
说明:有多个参数的lambda表达式,参数使用,隔开。Lambda主体是语句块的话须要使用{}。
2、Lambda 表达式实例
lambda示例1:
/* * Predicate<T>接口中boolean test(T t)方法只接收一个参数,返回值为boolean类型, * 故lambda表达式的输入参数只有一个参数:x,lambda的主体返回值为boolean类型 */ Predicate<Integer> atLeast5 = x -> x >= 5; System.out.println("传入参数是否大于5:" + atLeast5.test(6)); /* * BinaryOperator<T>接口中R apply(T t, U u)方法接收两个泛型参数,返回值为也为泛型 * 故lambda表达式输入Long参数有两个:x,y,lambda的主体返回值为Long类型 */ BinaryOperator<Long> addLongs = (x, y) -> { Long z = x + y; return z; }; System.out.println("计算传入两个Long参数的和:" + addLongs.apply(5l, 6l)); /* * Runnable接口中run()方法没有参数,有没有返回值,故lambda表达式没有参数,lambda主体也没有返回值 */ Runnable run1 = () -> System.out.println("这个方法就是run里面的方法"); run1.run(); try { final Integer value = 9; /* * 一、Callable<V>接口中V call() throws Exception方法没有参数,故lambda表达式也不能有输入参数, * 返回类型为泛型,故lambda主体须要返回指定Integer类型 * 二、由于.call()方法抛出异常,因此须要抛出或者捕获异常 * 三、lambda表达式使用外部参数,须要是final类型,及时外部参数没有定义为final类型, * 也会隐式的指定为final,故值或者引用地址不能修改。 */ Callable<Integer> call = () -> value; System.out.println("无参的方法,因此lambda的参数列表中不能传递参数:" + call.call()); } catch (Exception e) { e.printStackTrace(); }
lambda示例二:
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 multiplication = (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 x 5 = " + tester.operate(10, 5, multiplication)); System.out.println("10 / 5 = " + tester.operate(10, 5, division)); // 不用括号 GreetingService greetService1 = message -> System.out.println("Hello " + message); // 用括号 GreetingService greetService2 = (message) -> System.out.println("Hello " + message); greetService1.sayMessage("Runoob"); greetService2.sayMessage("Google"); }
// 定义接口和接口中为实现的方法 interface MathOperation {
int operation(int a, int b);
}
// 定义接口和接口中为实现的方法
interface GreetingService {
void sayMessage(String message);
}
// 定义方法,方法经过lambda表达式实现了接口中的方法 private int operate(int a, int b, MathOperation mathOperation){ return mathOperation.operation(a, b); } }
执行以上代码,输出结果为:
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google
使用 Lambda 表达式须要注意如下两点:
Lambda表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,咱们使用各类类型的Lambda表达式来定义MathOperation接口的方法。而后咱们定义了sayMessage的执行。
Lambda 表达式免去了使用匿名方法的麻烦,而且给予Java简单可是强大的函数化的编程能力。
Lambda表达式经常使用的场景是:函数式接口。函数式接口是指的只有一个抽象方法的接口,咱们经常使用的函数式接口有:
Runnable、Callable、PrivilegedAction、Comparator、FileFilter、PathMatcher、InvocationHandler、PropertyChangeListener、ActionListener、ChangeListener、Function、Predicate、BinaryOperator
3、lambda表达式中的变量做用域
lambda表达式只能引用标记了final的外层局部变量,这就是说不能在lambda内部修改定义在域外的局部变量,不然会编译错误。
在 Java8Tester.java 文件输入如下代码:
public class Java8Tester { final static String salutation = "Hello! "; public static void main(String args[]){
GreetingService greetService1 = message -> System.out.println(salutation + message); greetService1.sayMessage("Runoob"); //====================至关于下面============================== GreetingService g = new GreetingService() { @Override public void sayMessage(String message) { System.out.println(salutation + message); } }; g.sayMessage("jack");
} interface GreetingService { void sayMessage(String message); } }
执行以上脚本,输出结果为:
$ javac Java8Tester.java $ java Java8Tester Hello! Runoob Hello! jack
咱们也能够直接在 lambda 表达式中访问外层的局部变量:
public class Java8Tester { public static void main(String args[]) { final int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); // 输出结果为 3 } public interface Converter<T1, T2> { void convert(int i); } }
lambda表达式的局部变量能够不声明为final,可是不可被后面的代码修改(即隐性的具备final 的语义)
int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); num = 5; //报错信息:Local variable num defined in an enclosing scope must be final or effectively final
在 Lambda 表达式当中不容许声明一个与局部变量同名的参数或者局部变量。
String first = ""; Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错