java8新特性-lambda表达式

48 阅读4分钟

Lambda表达式初体验

当我们需要一个runnable但是又懒得去实现他的时候,就需要如下代码

Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("一点都不美观");
    }
};
r1.run();

但是自从java8出来之后,我们就有偷懒的法子了!

Runnable runnable = () -> {
    System.out.println("hello word");
};
runnable.run();

我们不用写方法名,不用写new以及后面的关键字,如果lambda表达式内只有一句代码的话我们甚至连大括号都不用写,例如上方的可以改为:

Runnable runnable = () -> System.out.println("hello word");
runnable.run();

lambda表达式是什么

lambda表达式是函数式接口的实例!什么是函数式接口呢?当接口中仅有一个抽象方法的时候,我们称这个接口是函数式接口。例如:

interface FunctionInterface {
    
    void test();
}

下面的接口也是函数式接口,因为toString方法是继承与Object的,并不是此接口的抽象方法

interface FunctionInterface {

    void test();
    
    String toString();
}

下面的接口就不是函数式接口了,因为这个接口有两个抽象方法

interface FunctionInterface {

    void test();
    
    void test2();
}

那么如果我们想写一个函数式接口时如何判断我们是否正确的写出来了呢?我们可以在类上添加一个@FunctionalInterface注解,这样如果我们的函数式接口写的不正确,就可以在编译时期发现,例如在idea中就会出现这样的提示:

image.png

为什么lambda表达式必须是函数式接口的实例

需要明确知道一点,lambda表达式就是一个对象,这个对象是可以调用方法的,当我们使用lambda的时候,不需要写方法名,那么编译器怎么知道我们的lambda表达式使用的是那个方法呢?如果说lambda表达式是函数式接口的对象,那就不用担心了,因为函数式接口只有一个抽象方法,我们在直接new接口的时候必须实现这个方法,所以这就是lambda表达式不用写方法名的原因,也是函数式接口为什么只能有一个抽象方法的原因!

lambda的写法

  • lambda表达式可以省略参数的类型,例如: Consumer接口是java内部定义的消费性接口
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

我们在使用consumer时,可以这样写

Consumer<String> con = new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
};

如果使用lambda表达式的话,我们可以这样写

Consumer<String> con = (String s) -> {
    System.out.println(s);
}

但是我们可以不用写参数的类型

Consumer<String> con = (s) -> {
    System.out.println(s);
}
  • 当只有一个参数时,可以省略参数两旁的括号,例如:
Consumer<String> con = s -> {
    System.out.println(s);
}
  • 当函数体中的代码只有一行的时候,我们可以不用写函数体的大括号
Consumer<String> consumer = s -> System.out.println(s);
  • 当函数有返回值的时候,并且函数体只有一句代码时,可以这样写
Supplier<String> supplier = () -> "hello world";

Supplier是java提供的一个函数式接口:

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

如果想要写return关键字的话,就必须加上大括号

Supplier<String> supplier = () -> {
    return "hello world";
};
  • 当我们在lambda表达式中只有一句代码,并且需要调用其他类或者对象的函数式时,如果满足下列条件,就可以写一个更高级的东西
    • lambda表达式实现的方法中的参数类型正是被调用的函数需要的参数类型
    • lambda表达式的返回值和被调用函数的返回值类型一样 例如:
Consumer<String> consumer = s -> System.out.println(s);

我们可以这样写:参数都是String类型,返回值都是void

Consumer<String> consumer = System.out::println;

lambda表达式的作用域

我们都知道在方法中new一个内部类时,如果这个内部类使用了方法中的局部变量或者是参数,那么在内部类中是不可以修改这个局部变量的,而在lambda表达式中,也是一样的!

image.png

在lambda中的this关键字,指向的是创建lambda表达式所在的方法的this对象!

public class LambdaTest {
    public void test1() {
        Runnable run = () -> {
          this.test2();
        };
    }

    public void test2() {
        System.out.println("hello world");
    }
  
}
public class LambdaTest {
    public void test1() {
        TestLambda testLambda = new TestLambda();
        testLambda.test3(()->{ this.test2(); });
    }
    
    public void test2() {
        System.out.println("hello world");
    }
    
}

class TestLambda {
    public void test3(Runnable runnable) {
        runnable.run();
    }
}