Lambda表达式

九、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);
}
相关文章
相关标签/搜索