Lambda表达式详解

1 Lambda表达式是Java8中的新特性

Java8中引入Lambda表达式,使得java可以函数式编程,在并发性能上迈出了实质性的一步。

什么是函数式编程?函数式编程(英语:functional programming)或称函数程序设计,又称泛函编程,是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。

ps:λ这个符号可以在搜狗输入法的符号中显示

而在面向对象编程中,面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的程序编程范型,同时也是一种程序开发的方法。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。

2 lambda表达式能干什么?

  @FunctionalInterface
  public interface InterfaceA {
       void show();
  }
  public class Demo {
      public static void main(String[] args) {
          InterfaceA a1 = new InterfaceA() {
  ​
              @Override
              public void show() {
                  System.out.println("匿名内部类的实现方式");
  ​
              }
          };
          a1.show();
          InterfaceA a2 = () -> {
              System.out.println("我是lambda表达式");
          };
  ​
          a2.show();
      }
  ​
  }
  ​

使用匿名内部类的一个问题是:当一个匿名内部类的实现非常简单,比如说接口只有一个抽象函数 ,那么匿名内部类的语法有点笨拙且不清晰。

使用下面这种方法的时候,不需要再使用new XXX(){}这种繁琐代码,不需要指出重写的方法的名字,也不需要给出重写方法的返回值类型,只要给出重写的方法括号以及括号里的形参列表即可

从上面介绍考科一看出,当使用Lambda表达式代替匿名内部类创建对象时,Lambda表达式的代码块将会代替实现抽象方法的方法体,Lambda表达式就相当于一个匿名方法

3 语法

(参数)->表达式 或 (参数)->{方法体;}

1.形参列表:

形参列表允许省略形参类型,若形参列表中只有一个参数,形参列表的圆括号也可以省略代码

2.箭头(->)

必须通过英文中划线号和大于符号组成

3.代码块:

如果代码块只包含一条语句,lambda表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束

lambda代码块只有一条return语句,甚至可以省略return关键字

lambda表达式需要返回值,而它的代码块中仅有一条省略了return的语句,lambda表达式会自动返回这条语句的结果

lambda表达式的写法:

  interface InterfaceA{
      void showA();
  }
  interface InterfaceB{
      void showB(int ia);
  }
  interface InterfaceC{ 
      void showC(int ib,int ic);
  }
  ​
  public class Demo {
  ​
      public static void main(String[] args) {
         InterfaceA a1 = ()->{
             System.out.println("接口A中方法");
         };
         //简化:
         InterfaceA a2 = ()-> System.out.println("接口A中方法");
         a1.showA();
         a2.showA();
         
         InterfaceB b1 = (int ia)->{
             System.out.println("接口B中方法:"+ia);
         };
         //简化:
         InterfaceB b2 = (ia)->{
             System.out.println("接口B中方法:"+ia);
         };
         InterfaceB b3 = (ia)-> System.out.println("接口B中方法:"+ia);
         
         InterfaceB b4 = ia -> System.out.println("接口B中方法:"+ia);
         b1.showB(10);
         b2.showB(20);
         b3.showB(30);
         b4.showB(40);
         
         
         InterfaceC c1 = (int ia,int ib)->{
             System.out.println("接口B中方法:"+ia+"和"+ib);
         };
         //简化
         InterfaceC c2 = (ia,ib)->{
             System.out.println("接口B中方法:"+ia+"和"+ib);
         };
         InterfaceC c3 = (ia,ib)-> System.out.println("接口B中方法:"+ia+"和"+ib);
         c1.showC(1,2);
         c2.showC(3,4);
         c3.showC(5,6);
      }
  ​
  }

lambda表达式就可以把函数当做函数的参数,代码(函数)当做数据(形参),这种特性满足上述需求。当要实现只有一个抽象函数的接口时,使用lambda表达式能够更灵活。

  interface InterfaceA{
      void showA();
  }
  interface InterfaceB{
      void showB(int ia);
  }
  interface InterfaceC{ 
      void showC(String ib,int ic);
  }
  ​
  public class Demo {
      public static void showInterfaceA(InterfaceA a) {
            System.out.println(a);
            a.showA();
      }
      public static void showInterfaceB(String message,InterfaceB b) {
          System.out.println("b信息:"+b+message);
          b.showB(100);
      }
      public static void showInterfaceC(String message,int phoneNumber,InterfaceC c) {
          System.out.println("c信息:"+c+message);
          c.showC(message, phoneNumber);
      }
  ​
      public static void main(String[] args) {
         showInterfaceA(()->System.out.println("可以作为参数传递"));
         showInterfaceB("接口B的lambda",b->System.out.println("可以作为参数传递"+b));
         showInterfaceC("接口C的lambda",88888888,(message,phoneNumber)->System.out.println("可以作为参数传递"+message+phoneNumber));
      }
  ​
  }

4 Lambda表达式与函数式接口

在上面的案例中.方法的参数的数据类型或是获取一个对象,但是在实际调用中我们传入的是一个lambda表达式,可以发现程序可以

正常编译,运行,这说明Lambda表达式实际上将会被当成一个"类型"的对象

Lambda表达式的类型,也被称为"目标类型(target type)",Lambda表达式的目标类型必须是"函数式接口(functional interface)"

ps:Java8新引入的概念,函数接口(functional interface)。它的定义是:一个接口,如果只有一个显式声明的抽象方法,那么它就是一个函数接口。一般用@FunctionalInterface标注出来 (也可以不标记),函数式接口可以包含多个default或static方法,但是只能声明一个抽象方法

@FuctionalInterface主要作用就是检查当前接口是不是函数接口

若想使用lambdaname目标必须是一个函数接口

5 Lambda表达式引用全局和局部变量

  @FunctionalInterface
  interface VariableTest {
      void sayMessage(String message);
  }
  ​
  public class Demo {
      //静态全局变量
      static String ms1 = "Hello!";
      //实例全局变量
      String ms2 = "Hello";
      public static void main(String[] args) {
          VariableTest vt = message -> {
              //直接在lambda表达式中调用全局变量
              System.out.println(message+ms1);
              System.out.println(message+new Demo().ms2);
              
          };
          vt.sayMessage("Lambda ");
          //局部变量-->在lambda表达式中抵用的不是局部变量而是常量
          String ms3 = "hello";
          VariableTest vt2 = message -> {
              //直接在lambda表达式中调用常量
              System.out.println(message+ms3);
              //ms3 = "llo";//修改报错 因为是常量,若是要修改这个 会提示显示声明添加 final
          };
          vt2.sayMessage("Lambda ");
          
          
      }
  ​
  }

 

6 方法引用与构造器引用

如果Lambda表达式的代码块只有一条代码,程序就可以省略Lambda表达式中的代码块的花括号

不仅如此,如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用

方法引用和构造器引用都需要使用::两个英文冒号

6.1 引用类方法

在函数式接口中定义的抽象方法,而方法的实现是触发某个类.方法(调用类方法的形式)来完成时可以使用

  @FunctionalInterface
  interface Converter{
      //将字符串转化对应的数
      Integer convert(String str);
  }
  ​
  public class Demo {
      public static void main(String[] args) {
         Converter c1 = str -> Integer.valueOf(str);
         Integer val1 = c1.convert("69");
         System.out.println(val1);
         //类方法应用来代替Lambda表达式,函数式接口中被实现方法的全部参数传递给改类方法作为参数
         Converter c2 = Integer::valueOf;
         Integer val2 = c2.convert("57");
         System.out.println(val2);
      }
  }
  ​

6.2 引用特定对象的实例方法

在函数式接口中定义的抽象方法,而方法的实现是触发对象.方法(调用类方法的形式)来完成时可以使用

  @FunctionalInterface
  interface ClassATest {
      void dispaly(String message);
  }
  class A{
      public void show(String message) {
          System.out.println("调用了特定对象的实例方法"+message);
      }
  }
  public class Demo {
      
      public static void main(String[] args) {
         ClassATest a1 = message -> new A().show(message);
         a1.dispaly("21");
         //特定对象的实例方法引用代替Lambda表达式,函数式接口中实现方法的全部参数传给该方法作为参数
         ClassATest a2 = new A()::show;
         a2.dispaly("17");
      }
  ​
  }

 

6.3 引用某类对象的实例方法

在函数式接口中定义的抽象方法,而方法的实现是触发是方法中第一个参数的对象.方法(调用类方法的形式)来完成时可以使用

  @FunctionalInterface
  interface SubString {
      String show(String str,int b,int c);
  }
  ​
  public class Demo {
      
      public static void main(String[] args) {
          SubString su1 = (str,b,c) -> str.substring(b, c);
          String val = su1.show("I Love U", 2, 6);
          System.out.println(val);
          //某类对象的实例方法引用代替Lambda表达式,函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传递给改方法
          SubString su2 = String::substring;
          String val2 = su2.show("I Love U", 2, 6);
          System.out.println(val2);
      }
  ​
  }
  ​

6.4 引用构造方法

在函数式接口中定义的抽象方法,而方法的返回值是一个对应类的实例

  @FunctionalInterface
  interface ClassBTest {
      B getInstance(String name,int age);
  }
  class B{
      private String name;
      private int age;
      public B(String name, int age) {
          this.name = name;
          this.age = age;
      }
      @Override
      public String toString() {
          return "我是创建好的对象年龄"+age+"姓名"+name;
      }
  }
  public class Demo {
      
      public static void main(String[] args) {
          ClassBTest cb1 = (name,age)->new B(name,age);
          B b1 = cb1.getInstance("张三", 18);
          System.out.println(b1);
          //构造方法引用代替lambda表达式,函数式接口中被实现方法的全部参数传该构造方法作为参数
          ClassBTest cb2 = B::new;
          B b2 = cb2.getInstance("李四", 19);
          System.out.println(b2);
          
      }
  ​
  }

7 Lambda表达式和匿名内部类的区别

1.匿名内部类可以为任意接口创建实例,不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可

但Lambda表达式只能为函数式接口创建实例(即只能有一个抽象方法)

2.匿名内部类可以为抽象类甚至是普通类创建实例

但Lambda表达式只能为函数式接口创建实例

3.匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认(default)方法

但Lambda表达式的代码块不允许调用接口中的默认(default)方法

8 Lambda表达式应用

  String[] name = {"张三", "李四",  "王五", "赵六"};  
  List players =  Arrays.asList(name);  
    
  // 以前的循环方式  
  for (String player : players) {  
       System.out.print(player + "; ");  
  }  
    
  // 使用 lambda 表达式以及函数操作  Java8中新方法
  players.forEach((player) -> System.out.print(player + "; "));  
     
  // 在 Java 8 中使用双冒号操作符  
  players.forEach(System.out::println
  ​
  ​
  // 1.1使用匿名内部类  
  new Thread(new Runnable() {  
      @Override  
      public void run() {  
          System.out.println("Hello world !");  
      }  
  }).start();  
    
  // 1.2使用 lambda expression  
  new Thread(() -> System.out.println("Hello world !")).start();  
    
  // 2.1使用匿名内部类  
  Runnable race1 = new Runnable() {  
      @Override  
      public void run() {  
          System.out.println("Hello world !");  
      }  
  };  
    
  // 2.2使用 lambda expression  
  Runnable race2 = () -> System.out.println("Hello world !");  
     
  // 直接调用 run 方法(没开新线程哦!)  
  race1.run();  
  race2.run();
  ​
  String[] names = {"张三", "李四",  "王五", "赵六"}; 
     
  // 1.1 使用匿名内部类根据 name 排序 names  
  Arrays.sort(names, new Comparator() {  
      @Override  
      public int compare(String s1, String s2) {  
          return (s1.compareTo(s2));  
      }  
  });
  // 1.2 使用 lambda 排序 names  
  Comparator sortByName = (String s1, String s2) -> (s1.compareTo(s2));  
  Arrays.sort(names, sortByName);  
    
  // 1.3 也可以采用如下形式:  
  Arrays.sort(names, (String s1, String s2) -> (s1.compareTo(s2))); 
  

你可能感兴趣的:(大数据)