为什么引入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). |