Lambda表达式

一个简单的应用场景

int aValue = 129;
String aString = "Hello.";

可以将一个值赋值给一个变量,但是把一段代码赋值给一个变量呢?先来看个例子

@FunctionalInterface
public interface WorkInterface {
    void doSomethind();
}

public class Main {
    public static void execute(WorkInterface worker){
        worker.doSomethind();
    }

    public static void main(String[] args) {
        execute(() -> {
            System.out.println("lambda test");
        });

        execute(new WorkInterface() {
            @Override
            public void doSomethind() {
                System.out.println("Old execute process");
            }
        });
    }
}

首先,需要有一个接口并且注解@FunctionalInterface,而这个接口就是此lambda表达式的变量类型,注解是为了保证这个接口有且只有一个函数,然后在Main主体中有如上两种方式的对比。再举一个更加常见的例子,

public class Main {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread from old type");
            }
        }).start();

        new Thread(() -> {
            System.out.println("Thread from lambda");
        }).start();
    }
}

很惊喜有没有,可以发现其实Runnable就是一个lambda接口

lambda表达式的一些结构

通过上面的例子大概可以知道lambda表达式长啥样了,现在说一些规定

  • 在lambda接口上最好加上注解@FunctionalInterface,这样可以保证是一个函数式接口并且别人无法在这里添加新的接口函数;当然也可以不加,在保证只有一个函数接口的情况下
  • 一个 Lambda 表达式可以有零个或多个参数
  • 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
    -** 所有参数需包含在圆括号内,参数之间用逗号相隔**。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
  • 空圆括号代表参数集为空。例如:() -> 42
  • 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
  • Lambda 表达式的主体可包含零条或多条语句
  • 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
  • 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空

为什么需要lambda表达式

  • 最直观:代码简洁

  • 内外部循环的区别

    List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);  
    
    //传统的遍历方式(外部循环)
    for (int number : numbers) {  
        System.out.println(number);  
    } 
    
    //lambda循环(内部循环)
    numbers.forEach((value) -> System.out.println(value)); 
    

    外部循环的缺点:

    1. 只能顺序处理List中的元素
    2. 不能充分利用多核CPU
    3. 不利于编译器优化

    而对比之下,内部循环的优点就暴露出来了:

    1. 不一定顺序处理List中的元素
    2. 可以并行,充分利用多核CPU
    3. 利于JIT编译器对代码进行优化
  • 传递行为,而不仅仅传递值!

  • Java 8提供了stream方法,我们可以通过对任何集合对象调用stream()方法获得Stream对象,Stream对象有别于Collections的几点如下:

    1. 不存储值:Streams不会存储值,它们从某个数据结构的流水线型操作中获取值
    2. 天生的函数编程特性:对Stream对象操作能得到一个结果,但是不会修改原始数据结构
    3. Laziness-seeking(延迟搜索):Stream的很多操作如filter、map、sort和duplicate removal(去重)可以延迟实现,意思是我们只要检查到满足要求的元素就可以返回
    4. 可选边界:Streams允许Client取足够多的元素直到满足某个条件为止。而Collections不能这么做

来些更加高级有用的例子

学Lambda表达式最好的方式就是找例子,来看例子1

@FunctionalInterface
public interface Checker {
    boolean check(Person person);
}

@FunctionalInterface
public interface Executor {
    void execute(Person person);
}

public class Main {

    public static void checkAndExecute(List personList, Checker checker, Executor executor){
        for (Person person : personList)
            if (checker.check(person))
                executor.execute(person);
    }

    public static void main(String[] args) {
        List personList = new ArrayList<>();
        personList.add(new Person("a"));
        personList.add(new Person("b"));
        personList.add(new Person("c"));

        checkAndExecute(personList,
                person -> {return person.getName().length() != 0;},
                person -> System.out.println(person.getName()));
    }
}

简洁了很多是吧,其实上面的接口还可以不用自己定义,直接使用Java8内置的Predicate和Consumer接口就可以了

例子2:参见https://www.zhihu.com/question/20125256

public class Main {
    public static Person UNKNOWN_PERSON = new Person("UNKNOWN_PERSON");

    public static void main(String[] args) {
        Person person = new Person("test");
        Optional personOptional = Optional.ofNullable(person);

        //如果person存在,那么直接打印输出person
        personOptional.ifPresent(System.out::print);
        //如果person存在,返回person;否则,返回UNKNOWN_PERSON对象
        personOptional.orElse(UNKNOWN_PERSON);
        //如果person,返回peron;否则,调用函数返回
        personOptional.orElseGet(() -> {return null; });
        //如果p存在,得到p.name;如果p.name存在,返回name.toLowerCase();所有的否则情况都是返回null
        personOptional.map(p -> p.getName())
                .map(name -> name.toLowerCase())
                .orElse(null);
    }
}

Lambda表达式和匿名类的区别

  • this

    对于匿名类,关键词 this 解读为匿名类本身;对于 Lambda 表达式,关键词 this 解读为写就 Lambda 的外部类

  • 编译方法不同

    Java 编译器编译 Lambda 表达式并将他们转化为类里面的私有函数,使用 Java 7 中新加的 invokedynamic 指令动态绑定该方法

java是怎么执行lambda表达式的

编译器编译 Lambda 表达式并将他们转化为类里面的私有函数,使用 Java 7 中新加的 invokedynamic 指令动态绑定该方法

你可能感兴趣的:(Lambda表达式)