Java8 - Lambda(λ) & Functional Interface

为什么引入Lambda?

简单的来说Lambda的引入是为了简化匿名类的编写

什么是Lambda表达式

  • Lambda表达式就是个匿名方法,就是说一个方法,但是没有定义名字,返回值,访问修饰符

例1:

public void m1(){
    System.out.println("Hello");
}

我们可以用Lambda表达式写成如下的方式

() -> System.out.println("Hello");
() -> {System.out.println("Hello");}

例2:

public void m2(int a, int b){
    System.out.println(a+b);
}

我们可以用Lambda表达式写成如下的方式

(int a, int b) -> System.out.println(a+b);
(a, b) -> {System.out.println(a+b);}

例3:

public String m3(String name){
   return "Hello " + name;
}

我们可以用Lambda表达式写成如下的方式

(String name) -> return  name;
(name) ->  name;

Functional Interface

说到Lambda,我们必须要提下Functional Interface, 那什么是Functional Interface呢?

  • 简单来说Functional interface只包含一个抽象方法,这样的一个解开我们称之为Functional Interface,其方法叫做Functional method 或者叫做simple abstract method (SAM)
    如果查看Java source code,你会发现Runnable 就是个Functional Interface
@FunctionalInterface
public interface Runnable {
  public abstract void run();
}

在Functional Interface 里面除了只能定义一个SAM之外,你随意定义多个default methods 还有static methods

@FunctionalInterface

你可以已经看到了,java8 为此定义了一个annotation,@FunctionalInterface, 被加了@FunctionalInterface的接口必须符合Functional interface的定义要求,有且只能有一个SAM, 否则会报错,比如

@FunctionalInterface
public interface Interf { 
  public void m1();
}

上面的定义不会报错,但是下面这两个定义会抱编译错误

@FunctionalInterface
public interface Interf { 
  public void m1();
  public void m2();
}
@FunctionalInterface 
  public interface interf{
}

对于继承接口,如果定义成Functional interface,也是同样的要求, 看下面的例子

@FunctionalInterface
public interface InterA { 
  public void m1();
}

@FunctionalInterface
public interface InterB extends InterA { 
}

上面的定义是OK的,但是下面的这个就会报编译错误,原因很简单,InterB 不符合有且只有一个SAM

@FunctionalInterface
public interface InterA { 
  public void m1();
}

@FunctionalInterface
public interface InterB extends InterA {
  public void m2(); 
}

InterB 如何去掉@FunctionalInterface,那InterB是个普通的interface,这个时候多定义几个接口是OK的,所以上面的那段代码,InterB去掉@FunctionalInterface,那么编译是可以通过的。

Functional Interface Vs Lambda

了解了Functional Interface, 来看看Functional Interface 跟Lambda 有什么关系。

一旦我们写了一个Lambda 表达式去调用它的方法, 那么就需要Functional Interface,我们知道当我们写Lambda的时候,我们不需要写方法的名字,那么我们到底是实现的哪个方法呢,这个时候就用到了Functional Interface的概念,能用Lambda写匿名function的,必须是个Functional interface,因为它只能定义一个抽象接口,所以Lambda表达式就一定是实现的那个接口。

例1: 传统写法

interface Interf { 
  public void method1() {}
}
  public class Demo implements Interface { 
    public void method1() { 
    System.out.println(“method1 ”);
  }
} 
 public class Test { 
 public static void main(String[] args) { 
  Interf i = new Demo();
  i.method1();
) }

Lambda写法

interface Interf { 
  public void method1() {}
}
 
 public class Test { 
 public static void main(String[] args) { 
  Interf i = () ->  System.out.println(“method1 ”);
  i.method1();
) }

例2: 传统写法

class MyRunnable implements Runnable { 
  public void main() { 
    for(int i=0; i<10; i++) { 
      System.out.println(“Child Thread”);
     } 
  } 
} 
class ThreadDemo { 
  public static void main(String[] args) {
    Runnable r = new myRunnable();
    Thread t = new Thread(r);
    t.start();
  }
}

Lambda写法


class ThreadDemo { 
  public static void main(String[] args) {
    Runnable r = () -> {
      for(int i=0; i<10; i++) { 
        System.out.println(" for lambda child thread");
     } 
    };
    Thread t = new Thread(r);
    t.start();
  }
}

匿名内部类 Vs Lambda表达式

我们同样可以用Lambda来简化匿名内部类的写法,如下的列子

传统写法

class ThreadDemo { 
  public static void main(String[] args) {
   
    Thread t = new Thread( new Runnable(){
        public void run(){
          for(int i=0; i<10; i++) { 
            System.out.println(" Child thread");
         } 
      }
    });
    t.start();
  }
}

Lambda写法

class ThreadDemo { 
  public static void main(String[] args) {
   
    Thread t = new Thread(() -> {
          for(int i=0; i<10; i++) { 
            System.out.println(" Child thread");
         } 
    });
    t.start();
  }
}

我们可以看到Lambda表达式可以简化匿名内部类的书写,可读性更高些。
但是 匿名内部类!=Lambda表达式:
-匿名内部类可以extends普通的类,抽象类,带有任意多抽象方法的接口,但是Lambda表达式只能实现带有一个抽象方法的接口

  • 匿名内部类可以声明instance变量;而Lambda不可以定义instance 变量,它定义的只是local 变量
  • 在匿名内部类里面this指的就是当前的匿名内部类; 而在Lambda表达式里面,this指定的是外部类,就是定义Lambda表达式的所在的那个类
Interface Interf{
  public void m1();
}

public class Demo{
  int i = 100;
  public void m2(){
      Interf i = () ->{
          int i =200;
          System.out.println(i); // print 200
          System.out.println(this.i) // print 100
      };
      i.m1();
  }
 public static void main(String[] args) {
    Demo demo = new Demo();
    demo.m2();
 }
}

☀ Lambda表达式,我们可以直接访问外部的类变量,外部的方法变量
☀ 但是外部的方法里面定义的local变量,在lambda表达式里面是final的,就是说Lambda表达式不可以修改外部方法的local变量,否则会出现编译错误,见下面的列子:

@FunctionalInterface 
public interface ForFunctionalInterface {
    
    public void name() ;
}
class Test{
    int yy = 77;
    
    public void test() {
        int tt = 100;
        ForFunctionalInterface it = () ->{
            int xx = 88;
            System.out.println(xx);
            System.out.println(tt);
            System.out.println(yy);
            xx = 11;
            yy = 99;
            tt = 200; // compile error
            };
        it.name();
    }
}

总结: 内部类跟Lambda表达式的区别

匿名内部类 Labmda表达式
匿名类 匿名方法
Anonymous inner class can extend Abstract and concrete classes lambda expression can’t extend Abstract and concrete classes
Anonymous inner class can implement An interface that contains any number of Abstract methods lambda expression can implement an Interface which contains single abstract method (Functional Interface)
Inside anonymous inner class we can Declare instance variables. Inside lambda expression we can’t Declare instance variables, whatever the variables declared are simply acts as local variables.
Anonymous inner classes can be Instantiated lambda expressions can’t be instantiated
Inside anonymous inner class “this”Always refers current anonymous Inner class object but not outer class Object. Inside lambda expression “this” Always refers current outer class object. That is enclosing class object.
Anonymous inner class is the best choice If we want to handle multiple methods. Lambda expression is the best Choice if we want to handle interface With single abstract method (Functional Interface).
In the case of anonymous inner class At the time of compilation a separate Dot class file will be generated(outerclass$1.class) At the time of compilation no dot Class file will be generated for Lambda expression. It simply converts in to private method outer class.
Memory allocated on demand Whenever we are creating an object Reside in permanent memory of JVM (Method Area).

你可能感兴趣的:(Java8 - Lambda(λ) & Functional Interface)