美文网首页
2019-11-06

2019-11-06

作者: 远飞的千纸鹤 | 来源:发表于2019-11-06 11:00 被阅读0次

    1.1 为什么需要Lambda表达式?

    使用Lambda表达式可以使代码变的更加紧凑,例如在Java中实现一个线程,只输出一个字符串Hello World!,我们的代码如下所示:

    public static void main(String[] args) throws Exception {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!");
            }
        }).start();
        TimeUnit.SECONDS.sleep(1000);
    }
    

    使用Lambda表达式之后代码变成如下形式:

    public static void main(String[] args) throws Exception {
        new Thread(() -> System.out.println("Hello World!")).start();
        TimeUnit.SECONDS.sleep(1000);
    }
    

    代码变的更紧凑了~

    1.2 Lambda表达式使用形式

    Parameters -> an expression
    lambda运算符 " -> ",可以叫他,“转到”或者 “成为”,运算符将表达式
    分为两部分,左边指定输入参数,右边是lambda的主体。
    
    1. 如果Lambda表达式中要执行多个语句块,需要将多个语句块以{}进行包装,如果有返回值,需要显示指定return语句,如下所示:
    Parameters -> {expressions;};
    
    1. Lambda表达式不需要参数,可以使用一个空括号表示,如下示例所示 一下
    () -> {for (int i = 0; i < 1000; i++) doSomething();};
    

    3.如果Lambda表达式参数类型可以省略,由编译器推断出来

    @FunctionalInterface
    interface Function3 {
        Integer add(Integer a,Integer b);
    }
    public class LambdaTest {
        public static void main(String[] args) {
                Function1  func  =  (a,b) -> a + b;
        }
    }
    

    1.3 函数式接口

    函数式接口是Java 8为支持Lambda表达式新发明的。
    特点:
    接口有且仅有一个抽象方法
    允许定义静态方法
    允许定义默认方法
    允许java.lang.Object中的public方法
    该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错粘贴代码

    @FunctionalInterface
    public interface MyInterface {
    
        //抽象方法
        public void hello();
    
        // java.lang.Object中的public方法
        public String toString();
    
        //默认方法
        public default void println(){
            System.out.println("hello world");
        }
    
        //静态方法
        public static void staticMethod(){
    
        }
    
    }
    
    public interface Iterable<T> {
        /**
         * Returns an iterator over elements of type {@code T}.
         *
         * @return an Iterator.
         */
        Iterator<T> iterator();
    
        /**
         * Performs the given action for each element of the {@code Iterable}
         * until all elements have been processed or the action throws an
         * exception.  Unless otherwise specified by the implementing class,
         * actions are performed in the order of iteration (if an iteration order
         * is specified).  Exceptions thrown by the action are relayed to the
         * caller.
         *
         * @implSpec
         * <p>The default implementation behaves as if:
         * <pre>{@code
         *     for (T t : this)
         *         action.accept(t);
         * }</pre>
         *
         * @param action The action to be performed for each element
         * @throws NullPointerException if the specified action is null
         * @since 1.8
         */
        default void forEach(Consumer<? super T> action) {
            Objects.requireNonNull(action);
            for (T t : this) {
                action.accept(t);
            }
        }
    

    在Java 8中向iterable这种接口增加一个方法,那么实现这个接口的所有类都要需实现一遍这个方法,那么Java 8需要更改的类就太多的,因此在Iterable接口里增加一个default实现,那么实现这个接口的所有类就都具有了这种实现,说白了,就是一个模板设计模式吧

    1.4 方法引用

    有时,我们需要执行的代码在某些类中已经存在,这时我们没必要再去写Lambda表达式,可以直接使用该方法,这种情况我们称之为方法引用

         List<Apple> apples = new ArrayList<Apple>(){
                {
                    add(new Apple(1));
                    add(new Apple(4));
                    add(new Apple(2));
                    add(new Apple(5));
                    add(new Apple(7));
                }
            };
            //使用Lambda表达式对重量排序 对苹果按照重量从小到大排序
            apples.sort((a1,a2) -> a1.getWeight().compareTo(a2.getWeight()));
    
    
            //使用方法引用和java.util.Comparator.comparing对重量排序
            apples.sort(comparing(Apple :: getWeight));
    
            // 方法引用主要分为三类
            //(1)指向静态方法的方法引用;
            List<String> list2  = Arrays.asList("1", "3", "5", "7");
            List<Integer> numbers = list2.stream().map(Integer :: parseInt).collect(Collectors.toList());
    
            //(2)指向任意类型实例方法的方法引用;
            List<Integer> weights = apples.stream().map(Apple::getWeight).collect(Collectors.toList())
            //(3)构造函数引用
            String str = "test";
            Stream.of(str).map(String::new).peek(System.out::println).findFirst();
    

    1.5 Lambda表达式作用域

    public class LambdaScope {
        public static void main(String[] args) {
            String[] datas = new String[] {"peng","Zhao","li"};
            datas = null;
            new Thread(() -> System.out.println(datas)).start();
        }
    
      Integer i = 0;
      i++;
            new Thread(() -> System.out.println(i++)).start();
    
            AtomicInteger j = new AtomicInteger();
            j.set(0);
            new Thread(() -> System.out.println(j.getAndIncrement())).start();
    

    这段代码会报datas不是final或者effectively final
    What is effectively final?
    Effectively final就是有效只读变量,意思是这个变量可以不加final关键字,但是这个变量必须是只读变量,即一旦定义后,在后面就不能再随意修改
    Java中内部类以及Lambda表达式中也不允许修改外部类中的变量,这是为了避免多线程情况下的race condition竞争条件

    1.6 Lambda实现原理分析

    @FunctionalInterface
    interface Print<T>{
        public void print(T t);
    }
    
    public class LambdaTest2 {
        public static void pringString(String s ,Print<String> print){
            print.print(s);
        }
    
        public static void main(String[] args){
            pringString("hello",x -> System.out.println(x));
        }
    }
    

    Lambda表达式经过编译之后,到底会生成什么东西呢?
    大家可以猜想一下Lambda表达式是不是转化成与之对应的函数式接口的一个实现类呢,然后通过多态的方式调用子类的实现呢?又或者是匿名内部类的实现?

    @FunctionalInterface
    interface Print<T>{
        public void print(T t);
    }
    class PrintImpl implements Print<String> {
        @Override
        public void print(String x) {
            System.out.println(x);
        }
    }
    public class LambdaTest2 {
        public static void pringString(String s ,Print<String> print){
            print.print(s);
        }
    
        public static void main(String[] args){
            pringString("hello",new PrintImpl());
        }
    }
    

    匿名内部类

    @FunctionalInterface
    interface Print<T>{
        public void print(T t);
    }
    public class LambdaTest2 {
        public static void pringString(String s ,Print<String> print){
            print.print(s);
        }
        public static void main(String[] args){
            pringString("hello", new Print<String>() {
                @Override
                public void print(String s) {
                    System.out.println(s);
                }
            });
        }
    }
    

    或者是这种

    @FunctionalInterface
    interface Print<T>{
        public void print(T t);
    }
    
    public class LambdaTest2 {
        public static void pringString(String s ,Print<String> print){
            print.print(s);
        }
    
      static final class PintImpl implements Print {
        public void print(String x) {
            staticMethod(x)
        }
            private PintImpl () {
            } 
        }
    
        private static void staticMethod(String s){
                System.out.println(s);
        }
    
        public static void main(String[] args){
            pringString("hello",new PintImpl() );
        }
    }
    

    上面的代码,除了在代码长度上长了点外,与用Lambda表达式实现的代码运行结果是一样的
    带着这个疑问,我们来尝试用jdk的bin目录下的一个字节码查看工具及反编译工具
    看下编译后的.class文件。

    • javac LambdaTest2.java 编译LambdaTest2.java文件得到LambdaTest2.class
    javac LambdaTest2.java
    Admin@PS20190604VZCZ MINGW64 /d/test
    $ dir
    LambdaTest2.class  LambdaTest2.java  Print.class
    
    • javap -p LambdaTest2.class
      上面命令中的-p表示输出所有类及成员,运行上面的命令后,得的结果如下所示:
    D:\test>javap -p LambdaTest2.class
    Compiled from "LambdaTest2.java"
    public class test.LambdaTest2 {
      public test.LambdaTest2();
      public static void pringString(java.lang.String, test.Print<java.lang.String>);
      public static void main(java.lang.String[]);
      private static void lambda$main$0(java.lang.String);
    }
    D:\test>$ javap -p Print.class
    Compiled from "LambdaTest2.java"
    interface test.Print<T> {
      public abstract void print(T);
    }
    

    编译器会根据Lambda表达式生成一个私有的静态函数
    private static void lambdamain0(java.lang.String);
    验证一下,

    package test;
    
    @FunctionalInterface
    interface Print<T>{
        public void print(T t);
    }
    
    public class LambdaTest2 {
    
        public static void pringString(String s ,Print<String> print){
            print.print(s);
        }
    
        private static void lambda$main$0(String s){
    
        }
    
        public static void main(String[] args){
    
            pringString("hello",x -> System.out.println(x));
        }
    }
    

    运行一下
    Error:(14, 25) java: 符号lambdamain0(java.lang.String)与test.LambdaTest2中的 compiler-synthesized 符号冲突

    D:\test>javac LambdaTest2.java
    LambdaTest2.java:14: 错误: 符号lambda$main$0(String)与LambdaTest2中的 compiler-synthesized 符号冲突
        private static void lambda$main$0(String s){
                            ^
    LambdaTest2.java:1: 错误: 符号lambda$main$0(String)与LambdaTest2中的 compiler-synthesized 符号冲突
    package test;
    ^
    2 个错误
    

    有了上面的内容,可以知道的是Lambda表达式在Java 9中首先会生成一个私有的静态函数,这个私有的静态函数干的就是Lambda表达式里面的内容,那么又是如何调用的生成的私有静态函数(lambdamain0(String s))呢?
    反编译一下LambdaTest.class 生成的字节码

    D:\test>javap
    用法: javap <options> <classes>
    其中, 可能的选项包括:
      -help  --help  -?        输出此用法消息
      -version                 版本信息
      -v  -verbose             输出附加信息
      -l                       输出行号和本地变量表
      -public                  仅显示公共类和成员
      -protected               显示受保护的/公共类和成员
      -package                 显示程序包/受保护的/公共类
                               和成员 (默认)
      -p  -private             显示所有类和成员
      -c                       对代码进行反汇编
      -s                       输出内部类型签名
      -sysinfo                 显示正在处理的类的
                               系统信息 (路径, 大小, 日期, MD5 散列)
      -constants               显示最终常量
      -classpath <path>        指定查找用户类文件的位置
      -cp <path>               指定查找用户类文件的位置
      -bootclasspath <path>    覆盖引导类文件的位置
    

    javap -p -v -c LambdaTest.class

    D:\test>javap -p -v -c LambdaTest2.class
    Classfile /D:/test/LambdaTest2.class
      Last modified 2019-11-10; size 1212 bytes
      MD5 checksum 9489e3d0504b929b245896c12faccf30
      Compiled from "LambdaTest2.java"
    public class test.LambdaTest2
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #9.#24         // java/lang/Object."<init>":()V
       #2 = InterfaceMethodref #25.#26        // test/Print.print:(Ljava/lang/Object;)V
       #3 = String             #27            // hello
       #4 = InvokeDynamic      #0:#33         // #0:print:()Ltest/Print;
       #5 = Methodref          #8.#34         // test/LambdaTest2.pringString:(Ljava/lang/String;Ltest/Print;)V
       #6 = Fieldref           #35.#36        // java/lang/System.out:Ljava/io/PrintStream;
       #7 = Methodref          #37.#38        // java/io/PrintStream.println:(Ljava/lang/String;)V
       #8 = Class              #39            // test/LambdaTest2
       #9 = Class              #40            // java/lang/Object
      #10 = Utf8               <init>
      #11 = Utf8               ()V
      #12 = Utf8               Code
      #13 = Utf8               LineNumberTable
      #14 = Utf8               pringString
      #15 = Utf8               (Ljava/lang/String;Ltest/Print;)V
      #16 = Utf8               Signature
      #17 = Utf8               (Ljava/lang/String;Ltest/Print<Ljava/lang/String;>;)V
      #18 = Utf8               main
      #19 = Utf8               ([Ljava/lang/String;)V
      #20 = Utf8               lambda$main$0
      #21 = Utf8               (Ljava/lang/String;)V
      #22 = Utf8               SourceFile
      #23 = Utf8               LambdaTest2.java
      #24 = NameAndType        #10:#11        // "<init>":()V
      #25 = Class              #41            // test/Print
      #26 = NameAndType        #42:#43        // print:(Ljava/lang/Object;)V
      #27 = Utf8               hello
      #28 = Utf8               BootstrapMethods
      #29 = MethodHandle       #6:#44         // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      #30 = MethodType         #43            //  (Ljava/lang/Object;)V
      #31 = MethodHandle       #6:#45         // invokestatic test/LambdaTest2.lambda$main$0:(Ljava/lang/String;)V
      #32 = MethodType         #21            //  (Ljava/lang/String;)V
      #33 = NameAndType        #42:#46        // print:()Ltest/Print;
      #34 = NameAndType        #14:#15        // pringString:(Ljava/lang/String;Ltest/Print;)V
      #35 = Class              #47            // java/lang/System
      #36 = NameAndType        #48:#49        // out:Ljava/io/PrintStream;
      #37 = Class              #50            // java/io/PrintStream
      #38 = NameAndType        #51:#21        // println:(Ljava/lang/String;)V
      #39 = Utf8               test/LambdaTest2
      #40 = Utf8               java/lang/Object
      #41 = Utf8               test/Print
      #42 = Utf8               print
      #43 = Utf8               (Ljava/lang/Object;)V
      #44 = Methodref          #52.#53        // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      #45 = Methodref          #8.#54         // test/LambdaTest2.lambda$main$0:(Ljava/lang/String;)V
      #46 = Utf8               ()Ltest/Print;
      #47 = Utf8               java/lang/System
      #48 = Utf8               out
      #49 = Utf8               Ljava/io/PrintStream;
      #50 = Utf8               java/io/PrintStream
      #51 = Utf8               println
      #52 = Class              #55            // java/lang/invoke/LambdaMetafactory
      #53 = NameAndType        #56:#60        // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      #54 = NameAndType        #20:#21        // lambda$main$0:(Ljava/lang/String;)V
      #55 = Utf8               java/lang/invoke/LambdaMetafactory
      #56 = Utf8               metafactory
      #57 = Class              #62            // java/lang/invoke/MethodHandles$Lookup
      #58 = Utf8               Lookup
      #59 = Utf8               InnerClasses
      #60 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      #61 = Class              #63            // java/lang/invoke/MethodHandles
      #62 = Utf8               java/lang/invoke/MethodHandles$Lookup
      #63 = Utf8               java/lang/invoke/MethodHandles
    {
      public test.LambdaTest2();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 8: 0
    
      public static void pringString(java.lang.String, test.Print<java.lang.String>);
        descriptor: (Ljava/lang/String;Ltest/Print;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=2, args_size=2
             0: aload_1
             1: aload_0
             2: invokeinterface #2,  2            // InterfaceMethod test/Print.print:(Ljava/lang/Object;)V
             7: return
          LineNumberTable:
            line 11: 0
            line 12: 7
        Signature: #17                          // (Ljava/lang/String;Ltest/Print<Ljava/lang/String;>;)V
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: ldc           #3                  // String hello
             2: invokedynamic #4,  0              // InvokeDynamic #0:print:()Ltest/Print;
             7: invokestatic  #5                  // Method pringString:(Ljava/lang/String;Ltest/Print;)V
            10: return
          LineNumberTable:
            line 16: 0
            line 17: 10
    
      private static void lambda$main$0(java.lang.String);
        descriptor: (Ljava/lang/String;)V
        flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
        Code:
          stack=2, locals=1, args_size=1
             0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: aload_0
             4: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             7: return
          LineNumberTable:
            line 16: 0
    }
    SourceFile: "LambdaTest2.java"
    InnerClasses:
         public static final #58= #57 of #61; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
    BootstrapMethods:
      0: #29 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
        Method arguments:
          #30 (Ljava/lang/Object;)V
          #31 invokestatic test/LambdaTest2.lambda$main$0:(Ljava/lang/String;)V
          #32 (Ljava/lang/String;)V
    

    注意反编译后main方法部分:
    补充一下invokedynamic , invokestatic 指令

    函数调用操作码 作用
    invokestatic 调用类方法(静态绑定,速度快)
    invokevirtual 指令调用一个对象的实例方法(动态绑定)
    invokespecial 指令调用实例初始化方法、私有方法、父类方法。(静态绑定,速度快)
    invokeinterface 调用引用类型为interface的实例方法(动态绑定)
    invokedynamic JDK 7引入的,主要是为了支持动态语言的方法调用
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: ldc           #3                  // String hello
         // 注意下面两句:通过实例调用 print
             2: invokedynamic #4,  0              // InvokeDynamic #0:print:()Ltest/Print;
        //调用静态方法 pringString
             7: invokestatic  #5                  // Method pringString:(Ljava/lang/String;Ltest/Print;)V
            10: return
          LineNumberTable:
            line 16: 0
            line 17: 10
    
    

    编译器会在每个Lambda表达式出现的地方插入一条indy(invokedynamic)指令,同时还必须在class文件里生成相应的CONSTANT_InvokeDynamic_info常量池项和BootstrapMethods属性等信息。这些信息将这条indy指令的bootstrap方法指向LambdaMetafactory.metafactory(...)方法。

    InnerClasses:
         public static final #58= #57 of #61; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
    BootstrapMethods:
      0: #29 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
        Method arguments:
          #30 (Ljava/lang/Object;)V
          #31 invokestatic test/LambdaTest2.lambda$main$0:(Ljava/lang/String;)V
          #32 (Ljava/lang/String;)V
    

    当JVM第一次执行到这条indy指令的时候,它会找到这条指令相应的bootstrap方法,然后调用该方法,得到一个CallSite。下面是metafactory()方法的代码

        /**
         * Facilitates the creation of simple "function objects" that implement one
         * or more interfaces by delegation to a provided {@link MethodHandle},
         * after appropriate type adaptation and partial evaluation of arguments.
         * Typically used as a <em>bootstrap method</em> for {@code invokedynamic}
         * call sites, to support the <em>lambda expression</em> and <em>method
         * reference expression</em> features of the Java Programming Language.
        *
        * <p>When the target of the {@code CallSite} returned from this method is
         * invoked, the resulting function objects are instances of a class which
         * implements the interface named by the return type of {@code invokedType},
         * declares a method with the name given by {@code invokedName} and the
         * signature given by {@code samMethodType}.  It may also override additional
         * methods from {@code Object}.
         * LambdaMetafactory方便我们创建简单的"函数对象",这些函数对象通过代理MethodHandle实现了一些        
         * 接口。
      * 当这个函数返回的CallSite被调用的时候,会产生一个类的实例
         */
      public static CallSite metafactory(MethodHandles.Lookup caller,
                                           String invokedName,
                                           MethodType invokedType,
                                           MethodType samMethodType,
                                           MethodHandle implMethod,
                                           MethodType instantiatedMethodType)
                throws LambdaConversionException {
            AbstractValidatingLambdaMetafactory mf;
            mf = new InnerClassLambdaMetafactory(caller, invokedType,
                                                 invokedName, samMethodType,
                                                 implMethod, instantiatedMethodType,
                                                 false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
            mf.validateMetafactoryArgs();
            return mf.buildCallSite();
        }
    

    在运行时加上-Djdk.internal.lambda.dumpProxyClasses,加上这个参数后,运行时,会将生成的内部类class码输出到一个文件中

    D:\>java -Djdk.internal.lambda.dumpProxyClasses  test.LambdaTest2
    hello
     D:\test 的目录
    
    2019/11/10  19:25    <DIR>          .
    2019/11/10  19:25    <DIR>          ..
    2019/11/10  19:25               398 LambdaTest2$$Lambda$1.class
    2019/11/10  18:53             1,212 LambdaTest2.class
    2019/11/10  18:53               341 LambdaTest2.java
    2019/11/10  18:53               296 Print.class
                   4 个文件          2,247 字节
                   2 个目录 100,959,272,960 可用字节
    

    反编译一下LambdaTest2$$Lambda$1.class,内容如下

    D:\test>javap -p LambdaTest2$$Lambda$1.class
    final class test.LambdaTest2$$Lambda$1 implements test.Print {
      private test.LambdaTest2$$Lambda$1();
      public void print(java.lang.Object);
    }
    
    D:\test>javap -c -p LambdaTest2$$Lambda$1.class
    final class test.LambdaTest2$$Lambda$1 implements test.Print {
      private test.LambdaTest2$$Lambda$1();
        Code:
           0: aload_0
           1: invokespecial #10                 // Method java/lang/Object."<init>":()V
           4: return
    
      public void print(java.lang.Object);
        Code:
           0: aload_1
           1: checkcast     #15                 // class java/lang/String
           4: invokestatic  #21                 // Method test/LambdaTest2.lambda$main$0:(Ljava/lang/String;)V
           7: return
    }
    

    代码还原

    package test;
    
    @FunctionalInterface
    interface Print<T>{
        public void print(T t);
    }
    
    public class LambdaTest2 {
        public static void pringString(String s ,Print<String> print){
            print.print(s);
        }
    
      static final class LambdaTest2$$Lambda$1 implements Print {
            public void print(Object obj) {
                LambdaTest2.lambda$main$0((String) obj);
            }
            private LambdaTest2$$Lambda$1() {
            }
        }
    
        private static void lambda$main$0(String s){
                System.out.println(s);
        }
    
        public static void main(String[] args){
            pringString("hello",new LambdaTest2$$Lambda$1() );
        }
    }
    

    至此,lambda表达式的脱糖过程已经了解完成。
    为什么要绕一圈生成一个内部类的动态调用点然后执行,而不是直接把lambda编译成内部类,也许是为了减少编译后的文件数

    1.7 Lambda在智能系统中的使用场景

    接口只有查询操作,通常会优先读从库,调用ThreadLocalUtils工具类里面的runWithSecondaryPreferredMode方法。看下这里对Runnable函数式接口的使用。

    Service
    public class HistoryApiServiceImpl extends HistoryApiService {
        private static final Logger LOGGER = LoggerFactory.getLogger(HistoryApiServiceImpl.class);
    
        @Autowired
        private HistoryService historyService;
    
        @Override
        public Response getCustomerFollowupList(CustomerFollowUpReq body, SecurityContext securityContext, HttpServletRequest request) throws NotFoundException {
            String agentId = checkAgent(request);
            CustomerFollowUps customerFollowUps = ThreadLocalUtils.runWithSecondaryPreferredMode(() ->historyService.getCustomerFollowupList(body,agentId));
            return ResponseHelper.ok(customerFollowUps);
        }
    
        @Override
        public Response getMLSHouseList(UnitCondition body, SecurityContext securityContext, HttpServletRequest request) throws NotFoundException {
            String agentId = checkAgent(request);
            MLSHouseListRsp rsp =  ThreadLocalUtils.runWithSecondaryPreferredMode(() -> historyService.getMLSHouseList(body, agentId));
            return ResponseHelper.ok(rsp);
        }
    
        @Override
        public Response getMlsHouseFollowUpList(FollowUpReq body, SecurityContext securityContext, HttpServletRequest request) throws NotFoundException {
            String agentId = checkAgent(request);
            FollowUpRsp followUpRsp = ThreadLocalUtils.runWithSecondaryPreferredMode(() -> historyService.getMlsHouseFollowUpList(body,agentId));
            return ResponseHelper.ok(followUpRsp);
        }
    

    ThreadLocalUtils

    package com.fanggeek.common.utils.thread;
    
    import com.fanggeek.common.alarm.model.RequestInfo;
    import java.util.function.Supplier;
    import org.apache.commons.lang3.exception.ExceptionUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class ThreadLocalUtils {
    
        public static void runWithSecondaryPreferredMode(Runnable runnable) {
            try {
                getSomeSwitch().turnOnSecondaryPreferred();
                runnable.run();
            } catch (Exception var5) {
                LOGGER.error("runWithSecondaryPreferredMode Runnable", var5);
                throw var5;
            } finally {
                getSomeSwitch().turnOffSecondaryPreferred();
            }
    
        }
    
        public static <T> T runWithSecondaryPreferredMode(Supplier<T> xxx) {
            Object t = null;
    
            try {
                getSomeSwitch().turnOnSecondaryPreferred();
                t = xxx.get();
            } catch (Exception var6) {
                LOGGER.error("runWithSecondaryPreferredMode Supplier ", var6);
                throw var6;
            } finally {
                getSomeSwitch().turnOffSecondaryPreferred();
            }
    
            return t;
        }
    
        public static void runWithStringValue(Runnable runnable, String value) {
            try {
                setString(value);
                runnable.run();
            } catch (Exception var6) {
                LOGGER.error("runWithStringValue Runnable {}", ExceptionUtils.getStackTrace(var6));
                throw var6;
            } finally {
                stringRemove();
            }
        }
    
        public static <T> T runWithStringValue(Supplier<T> xxx, String value) {
            Object t = null;
    
            try {
                setString(value);
                t = xxx.get();
            } catch (Exception var7) {
                LOGGER.error("runWithStringValue Runnable {}", ExceptionUtils.getStackTrace(var7));
                throw var7;
            } finally {
                stringRemove();
            }
    
            return t;
        }
    }
    
     *
     * @author  Arthur van Hoff
     * @see     java.lang.Thread
     * @see     java.util.concurrent.Callable
     * @since   JDK1.0
     */
    @FunctionalInterface
    public interface Runnable {
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see     java.lang.Thread#run()
         */
        public abstract void run();
    }
    
    D:\test2>javap -p RunableTest.class
    Compiled from "RunableTest.java"
    public class test2.RunableTest {
      public test2.RunableTest();
      public static void main(java.lang.String[]);
      private static void lambda$main$0();
    }
    
    D:\test2>javap -p RunableTest$$Lambda$1.class
    final class test2.RunableTest$$Lambda$1 implements java.lang.Runnable {
      private test2.RunableTest$$Lambda$1();
      public void run();
    }
    
    D:\test2>
    
    package test2;
    
    public class RunableTest {
    
        public static void main(String[] args){
            Runnable runnable= () -> System.out.println("helloword");
            runnable.run();
        }
    }
    

    Runnable接口常用于创建一个线程,不需要传递参数,也没有返回结果。
    智能系统中,异步去执行某个操作,不需要返回结果可以用AsyncUtils.run方法

    package java.util.function;
    
    /**
     * Represents a supplier of results.
     *这是供给型函数接口.
     * <p>There is no requirement that a new or distinct result be returned each
     * time the supplier is invoked.
     *特点:
     * (1)只有返回值
     * (2)没有输入参数
     * get()方法被调用时,对于一定要new出一个新对象 or 生成一个和之前结果不同的值 这两方面,都没有强制规 
      *定.
     * 这一接口函数的功能方法为:get()
     * <p>This is a <a href="package-summary.html">functional interface</a>
     * whose functional method is {@link #get()}.
     * @param <T> the type of results supplied by this supplier
     *
     * @since 1.8
     */
    @FunctionalInterface
    public interface Supplier<T> {
    
        /**
         * Gets a result.
         *
         * @return a result
         */
        T get();
    }
    
    package test3;
    
    import java.util.concurrent.Callable;
    
    public class CallableTest {
    
        public static void main(String[] args) throws Exception {
            Callable<Integer> callable= () -> new Integer(1);
            System.out.println(callable.call());
        }
    }
    
    D:\test3>javap -p CallableTest.class
    Compiled from "CallableTest.java"
    public class test3.CallableTest {
      public test3.CallableTest();
      public static void main(java.lang.String[]) throws java.lang.Exception;
      private static java.lang.Integer lambda$main$0() throws java.lang.Exception;
    }
    
    D:\test3>javap -p CallableTest$$Lambda$1.class
    final class test3.CallableTest$$Lambda$1 implements java.util.concurrent.Callable {
      private test3.CallableTest$$Lambda$1();
      public java.lang.Object call();
    }
    

    重新放盘业务的使用

        private void modifyHouse4ReUpload(TeamUnitDocument teamUnitDoc) {
            //在当前线程中设置一个重新放盘的标识
            ThreadLocalUtils.runWithStringValue(() -> teamHouseV2Service.modifyHouse(teamUnitDoc), TeamConstant.REUPLOAD_THREAD_NAME);
        }
    

    Null工具类中Supplier<T>函数式接口的使用

    Null.ofInt(() -> body.getPrivateState())

    public class Null {
    public static final <T> T ofInt(Supplier<T> expr){
            Supplier<T> defaultValues =  ()-> (T)new Integer(0);
            return of(expr, defaultValues);
    }
    
    public static final <T> T ofDouble(Supplier<T> expr){
            Supplier<T> defaultValues =  ()-> (T)new Double(0.0D);
            return of(expr, defaultValues);
        }   
        
    public static final <T> T ofLong(Supplier<T> expr){
            Supplier<T> defaultValues =  ()-> (T)new Long(0L);
                    return of(expr, defaultValues);
          }
    
    public static final <T> T of(Supplier<T> expr, Supplier<T> defaultValue){
            try{
                T result = expr.get();
                
                if(result == null){
                    return defaultValue.get();
                }
                return result;
            }catch (NullPointerException|IndexOutOfBoundsException e) {
                return defaultValue.get();
            }catch (Exception e) {
                log.error("ObjectHelper get error.", e);
                throw new RuntimeException(e);
            }
    }   
    }
    

    AsyncUtils

    public class AsyncUtils {
        
        private static final Logger LOGGER =LoggerFactory.getLogger(AsyncUtils.class);
        
        private static final ExecutorService EXECUTOR_SERVICE_SINGLE = (ExecutorService) Executors.newSingleThreadExecutor();
        
        private static final ScheduledExecutorService EXECUTOR_SERVICE_SCHEDULED = Executors.newScheduledThreadPool(20); 
        
        private static final ExecutorService EXECUTOR_SERVICE_ACTION_LOG = (ExecutorService) Executors.newFixedThreadPool(2);
    
        /*
        房源列表专用,线程池阻塞队列
         */
        private static final LinkedBlockingQueue<Runnable> HOUSE_LIST_WAITING_QUEUE = new LinkedBlockingQueue<>();
    
        /**
         * <br>线程池(房源列表专用),目前大小为 10
         */
        public static final ExecutorService HOUSE_LIST_EXECUTOR_SERVICE = new ThreadPoolExecutor(10, 20,
                30000, TimeUnit.MILLISECONDS, HOUSE_LIST_WAITING_QUEUE);
        
        /**
         * 因为 Runnable 接口是 函数式接口(可以显式的加上@FunctionalInterface, 也可以不加,但是这个接口有且仅有一个方法)
         * <br>所以可以通过 lambda 表达式来快速构造 () -> xxxx
         * @param task
         */
        public static void run(Runnable task){
            AsyncExecutor.run(task);
        }
        
        public static void runActionLog(Runnable task){
            EXECUTOR_SERVICE_ACTION_LOG.execute(task);
        }
        
        public static <T> Future<T> submit(Callable<T> task){
            return AsyncExecutor.submit(task);
        }
    

    AsyncExecutor

    /**
     * 异步任务 执行器
     * @author YellowTail
     * @since 2019-04-24
     */
    public class AsyncExecutor {
        
        /**
         * <br>异步任务提交后,都是在这个 链式 阻塞队列里 存着
         */
        private static final LinkedBlockingQueue<Runnable> TASK_WAITING_QUEUE = new LinkedBlockingQueue<>();
        
        /**
         * <br>定长线程池,目前大小为 10
         */
        public static final ExecutorService EXECUTOR_SERVICE = new ThreadPoolExecutor(10, 10,
                0L, TimeUnit.MILLISECONDS, TASK_WAITING_QUEUE);
        
        /**
         * <br>long 类型的 原子计数器,inc 操作基于 CAS
         */
        private static final AtomicLong TOTAL_COUNT = new AtomicLong();
    
        public static void run(Runnable task){
            TOTAL_COUNT.incrementAndGet();
            
            EXECUTOR_SERVICE.execute(task);
        }
        
        public static <T> Future<T> submit(Callable<T> task){
            TOTAL_COUNT.incrementAndGet();
            
            return EXECUTOR_SERVICE.submit(task);
        }
    

    Callable接口

    /**
     * A task that returns a result and may throw an exception.
     * Implementors define a single method with no arguments called
     * {@code call}.
     *
     * <p>The {@code Callable} interface is similar to {@link
     * java.lang.Runnable}, in that both are designed for classes whose
     * instances are potentially executed by another thread.  A
     * {@code Runnable}, however, does not return a result and cannot
     * throw a checked exception.
     *
     * <p>The {@link Executors} class contains utility methods to
     * convert from other common forms to {@code Callable} classes.
     *
     * @see Executor
     * @since 1.5
     * @author Doug Lea
     * @param <V> the result type of method {@code call}
     */
    @FunctionalInterface
    public interface Callable<V> {
        /**
         * Computes a result, or throws an exception if unable to do so.
         *
         * @return computed result
         * @throws Exception if unable to compute a result
         */
        V call() throws Exception;
    }
    

    Supplier<T>和Callable<V>接口都是 参数为空,返回一个结果
    One basic difference between the 2 interfaces is that Callable allows checked exceptions to be thrown from within the implementation of it, while Supplier doesn't.

    @FunctionalInterface
    public interface Predicate<T> {
    
        /**
         * Evaluates this predicate on the given argument.
         *
         * @param t the input argument
         * @return {@code true} if the input argument matches the predicate,
         * otherwise {@code false}
         */
        boolean test(T t);
    }
    
    @FunctionalInterface
    public interface Function<T, R> {
    
        /**
         * Applies this function to the given argument.
         *
         * @param t the function argument
         * @return the function result
         */
        R apply(T t);
    }
    
    @FunctionalInterface
    public interface Consumer<T>{
     //接受 t 对象,无返回值
     void accept(T t);
        
    //默认的组合方法,参数和返回值都是 Consumer 类型,先调用自己的 accept()方法,再调用参数的 accept()方法
     default Consumer<T>  andThen(Consumer<T>  after) {
     Objects.requireNonNull(after);
     return (T t) -> { accept(t); after.accept(t); };
     }
    }
    ``

    相关文章

      网友评论

          本文标题:2019-11-06

          本文链接:https://www.haomeiwen.com/subject/nhxubctx.html