九、Lambda表达式java
java是强类型语言,必须指定类型算法
(String first, String second)->first.length()-second.length();
若是lambda表达式的表达体执行一个没法用一个表达式表示的计算,那么用{}包裹代码并明确些上return语句。数组
1 (String first, String second) -> {
2 int difference = first.length() < second.length(); 3 if(difference <0) return -1; 4 else if(difference >0) return 1; 5 else return 0; 6 }
若是lambda表达式没有参数,则写一个空的小括号。安全
1 Runnable task = () -> { for(int i=0;i<1000;i++) doWork();}
若是lambda表达式的参数类型能够被对到出来,则能够省略类型。 多线程
Comparator<String> comp = (first, second) -> first.length() - second.lenght();
这里由于lambda表达式被赋值给String比较器,编译器能够推断出类型。若是某方法只有一个参数,且参数类型可推导,甚至能够省略小括号。闭包
1 EventHandler<ActionEvent> listener = event -> System.out.println();
永远不要为lambda表达式指定返回类型,编译器会从lambda表达式推断出类型,并检查返回类型是否与指望的类型匹配。并发
十、函数式接口app
不管什么时候,当你指望只有一个抽象方法的接口对象时,就能够提供一个lambda表达式。这样的接口被称为函数式接口dom
Arrays.sort方法。该方法的第二个参数要求一个Comparator接口的实例。直接提供一个lambda:函数
1 Array.sort(words,(first, second) -> first.length()-second.length());
表达式背后,Arrays.sort方法的第二个参数变量接受一个实现了Comparator<String>接口的类的实例。调用该对象的compare方法会执行lambda表达式中的代码。这些对象和类的管理彻底依赖于实现,并是高度优化的。
在Java中,lambda表达式只能将其放入类型为函数式接口的变量中,这样他就被转换为该接口的实例。
不能将lambda表达式复制给Object的变量,由于Object是类,不是函数式接口。
十一、方法引用
当传递给其余代码的操做已经有实现的方法了,可使用方法引用
1 Arrays.sort(Strings, (x,y) -> x.compareToIgnoreCase(y));
2 //也能够传入方法表达式:
3 Arrays.sort(strings, String::compareToIgnoreCase);
Object定义了inNull方法,返回x==null的值。
list.removeIf(Object::isNull);
1 list.forEach(x-> System.out.println(x));
2 //能够写成
3 list.forEach(System.out::println);
操做符::将方法名与类或对象分隔开
一、类::实例方法 第一个参数变成方法的接受者,其余的参数也传递给该方法。
String::compareToIgnoreCase 等同于 (x,y)->x.compareToIgnoreCase(y)
二、类::静态方法 全部的参数传递给静态方法。
Objects::isNull 等同于 x->Objects.isNull(x)
三、对象::实例方法 在给定的对象上调用方法,而且参数传递给实例方法。
System.out::println 等同于 x->System.out.println(x)
在内部类中,能够用"外部类.this::方法名称"捕获外部类的this引用。
使用数组构造函数能够绕过java的限制,构造一个泛型数组。
Employee[] buttons = stream.toArray(Employee[]::new)
十二、使用lambda表达式
一、实现延迟执行
使用lambda表达式就在于延迟执行。若是想当即执行一段代码,无需将代码封装进lambda,能够直接调用。
延迟执行的缘由:(其实就是支持函数式变成的接口有哪些)
在另外一个单独的线程中运行代码
屡次运行代码
在算法的恰当时刻运行代码(排序中的比较操做)
当某些状况发生时运行代码(按钮被点击,数据到达)
只有在须要时才运行的代码
如:想将一个行为重复10次
repeat(10,()->System.out.println("a"));
//要选择一个函数式接口来接受lambda表达式
public static void repeat(int n, Runnalbe action){ for(int i=0;i<n;i++) action.run(); } //当action.run()被调用时,lambda表达式体被执行。
二、经常使用的函数式接口
大多数标准的函数式接口都有用来产生或者组装函数的非抽象方法。Predicate.isEqual(a)与a::equals相同
表3-2 为原始类型提供的函数式接口:p、q 为int、long、double 类型,P、Q 为Int、Long、Double 类型
好比能够用IntConsumer代替Consumer<Integer>
三、实现本身的函数式接口
若是想用颜色填充一张图片,用户能够提供为每一个像素产生颜色的函数,标准类型中没有(int,int)->color。这时能够用BiFunction<Integer,Integer,Color>可是这样会自动装箱。
或者定义一个新的接口:
1 @FunctionalInterface
2 public interface PixelFunction{ 3 Color apply(int x, int y) 4 }
用@FunctionalInterface注解标记函数式接口,这样编译器会检查出被注释的实体是一个带有单个抽象方法的接口。JavaDoc页面也会有函数式接口的声明。
实现以下:
1 BufferedImage createImage(int width, int height, PixelFunction f){
2 BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); 3 for(int x =0;x<width;x++){ 4 for(int y =0;y<height;y++){ 5 Color color = f.apply(x,y)//这里f就是传入的lambda表达式 6 image.setRGB(x,y,color.getRGB()); 7 } 8 retrun image; 9 } 10 } 11 12 //调用 13 BufferedImage frenchFlag = createImage(150,100, 14 (x,y)->x<50?Color.Blue:x<100?Color.WHITE:Color.Red);
1三、lambda表达式的做用域
lambda表达式的方法体与嵌套代码块有相同的做用域。在lambda中不容许声明一个与局部变量同名的参数或局部变量。
int first = 0; Comparator<String> comp =(first,second) ->first.length() - second.length(); //错误:变量first已经定义了
lambda中的this表明的是建立lambda表达式方法的this参数。
1 public class Application(){ 2 public void dowork(){ 3 Runnable runner = () ->{...;Sysout(this.toString);} 4 } 5 }
this.toString是调用Application对象的toString()方法,lambda的做用域被嵌套在dowork方法中。
1四、访问来自闭合做用域的变量
在lambda中访问来自闭合方法或类的变量。
1 public static void repeatMessage(String text,int count){ 2 Runnable r = () -> { 3 for(int i =0;i<count;i++){ 4 System.out.println(text); 5 } 6 }; 7 new Thread(r).start(); 8 }
lambda访问了定义在闭合域,而不是定义在表达式自身中的参数标量text
若是repeatMessage(“A”,1000)//在单独的线程中将A打印1000次
若是lambda在repeatMessage返回以后好久才运行,此时参数变量已经消失了。
1五、lambda表达式的理解
lambda表达式分为三个部分:一、代码块 二、参数 三、自由变量的值(既不是参数标量,也不是代码内部定义的变量)
在repeatMessage中lambda由两个自由变量,text和count。这些变量其实已经被lambda捕获了(经过一个具体的实现细节来完成捕获工做,将lambda转变为带有一个方法的对象,这样自由变量的值就能够复制到对象的实例变量中)
闭包(closure)描述带有自由变量值得代码块的技术,在java中lambda就是闭包
lambda只能引用哪些值不会改变的变量,只能捕获变量的值,而不是变量的引用。
for(int i=0;i<n;i++){ new Thread(()->System.out.println(i)).start(); //错误,不能捕获i }
lambda只能访问来自闭合做用域的final局部变量。(一样的规则适用于被局部内部类捕获的变量)
注意:加强的for循环中的变量是有效final的,由于他的做用域是单个迭代。
for(String arg: args){ new Thread(()->System.out.println(arg)).start(); //能够捕获 }
做为“有效final”规则的结果,lambda不能改变任何捕获的变量。能够有效的防止多线程下的冲突。
禁止修改变量只是针对局部变量。若是count是实例变量或者外部类的静态变量,及时并发时结果不肯定,也不会有错误报告。
//能够用长度为1的数组绕过限制 int[] counter = new int[1]; button.setOnAction(event->counter[0]++); //counter是有效final的,一直引用赞成个数组。可是是线程不安全的。
1六、高阶函数
处理货返回函数的函数称为高阶函数。
一、返回函数的方法
假如想对字符串进行升序或降序排序。建立一个会产生正确比较器(comparator)的方法
1 public static Comparator<String> compareInDirection(int direction){ 2 return (x,y) -> direction * x.compareTo(y); 3 }
能够传递给另外一个指望这种借口的方法
Arrays.sort(friends, comparaInDirection(-1));
二、修改函数的方法
1 list.sort(reverse(compare())); 2 3 public static Comparator<Integer> compare() { 4 return (x,y) -> x.compareTo(y); 5 } 6 7 public static Comparator<Integer> reverse(Comparator<Integer> comp){ 8 return (x,y) -> comp.compare(y, x); 9 }
三、Comparator
Comparator接口有不少有用的静态方法,他们是产生comparator的高阶函数。
comparing方法接受“key提取器”函数,将类型T映射到可比较的类型。函数能够引用到被比较的对象,而且是在返回的key上进行的比较。
1 Arrays.sort(people, Comparator.comparing(Person::getName)); 2 Arrays.sort(people, Comparator.comparing(Person::getName).thenComparing(Person::getFirstName)); 3 // 根据名字长度进行排序 4 Arrays.sort(people, Comparator.comparing(Person::getName),(s,t)->s.lenght-t.length()));
为了不装箱可使用comparingInt
1 Arrays.sort(people,Comparator.comparingInt(p->p.getName().length()));
为了防止空指针异常,当有空对象时可使用nullsFirst和nullsLast。使用naturalOrder和reserveOrder能够返回正序和逆序两种比较器(自动推断类型)。
1 Arrays.sort(people, comparing(Person::getMiddleName, nullsFirst(naturalOrder())));
1七、局部内部类
在方法里定义的类为局部类。
//产生给定范围的无限序列随机整数 public static IntSequence randomInts(int low, int high) //由于IntSequence是个接口,因此必须返回实现该接口的某个类的对象 private static Random generator = new Random(); public static InSequence randomInts(int low, int high){ Class RandomSequence implements IntSequence{ public int next() {return low+generator.nextInt(high-low+1);} public boolean hasNext() {return true;} } return new RandomSequence(); }
局部类没有声明为public或private,由于对方法外部而言它是永远不可访问的。若是将RandomSequence转变为嵌套类,将不得不提供一个显示的构造函数,接受闭合做用域变量,可是用内部类就不用。
//如今可使用lambda表达式 public static IntSequence randomInts(int low, int high){ return ()->low + generator.nextInt(high - low + 1); }