android lambda的使用总结及运行原理

为了支持函数式编程,Java 8引入了Lambda表达式,Android N已经开始支持Java 8 了。Java 8中的新特性,是开发者们的一大福音,从此我们可以happy的在代码中使用Lambda了,调用Stream等。本篇文章主要介绍Lambda的特性,实现原现,使用方法,关于Java8 新特性及用法,会再开一篇博文进行总结。使用Lambda可以大大减少代码的编写,只关注最重要的部分。虽然使代码的可读性变差,但用习惯了就会喜欢上Lambda表达式,它使代码变得干净整洁了不是一点半点。

既然大家都用上了lambda表达式,为什么我还要写这篇文章呢,咱们开发人员当然不能老是拿来主义,知其然还得知其所以然。在Java 8中到底是如何实现Lambda表达式的呢? Lambda表达式经过编译之后,到底会生成什么东西呢? 它和匿名内部类有什么区别呢?如果掌握了它的运行原理,以后面试被问起来,也能说出个1,2,3来不是。

1、lambda表达式是使用内部类来实现的?

lambda表达式不是简单的匿名内部类,因为使用匿名内部类,编译器会为每一个匿名内部类创建一个类文件,而类在使用前需要加载类文件并进行验证,这个过程会影响应用的启动性能。类文件加载很可能是一个耗时的操作,若lambda采用匿名内部类实现,会使应用内存占用增加,同时也会使lambda表达式与匿名内部类的字节码生成机制绑定。所以lambda表达式不是采用匿名内部类来实现。
我们通过分析下面代码:

public class Lambda {
    Function f = s -> Integer.parseInt(s);
}

查看上面的类经过编译之后生成的字节码:

0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: aload_0
5: invokedynamic #2, 0 // InvokeDynamic
                  #0:apply:()Ljava/util/function/Function;
10: putfield #3 // Field f:Ljava/util/function/Function;
13: return

可以看到lambda使用了java中的动态指令,所以lambda内部并不是使用内部类来实现的。

2、lambda表达式是怎么运行的?

lambda表达式将翻译策略推迟到运行时,主要是将表达式转成字节码invoked dynamic 指令,如上面编译成的字节码,主要有以下两步:
1)生成一个invoked dynamic调用点(dynamic工厂),当lambda表达式被调用时,会返回一个lambda表达式转化成的函数式接口实例;
2)将lambda表达式的方法体转换成方法供invoked dynamic指令调用。
对于大多数情况下,lambda表达式要比匿名内部类性能更优。

3、 lambda表达式是怎么翻译成机器识别的代码?

对于lambda表达式翻译成实际运行代码,分为对变量捕获和不对变量捕获方法,即是否需要访问外部变量。
对于下面的表达式:

public class Lambda {
    Function f = s -> Integer.parseInt(s);
}

1)对于不进行变量捕获的lambda表达式,其方法实现会被提取到一个与之具有相同签名的静态方法中,这个静态方法和lambda表达式位同一个类上。
上面的表达式会变成:

static Integer lambda$1(String s) {
    return Integer.parseInt(s);
}

2)对于捕获变量的lambda表达式,lambda表达式依然会被提取到一个静态方法中,被捕获的变量会同正常的参数一样传入到这个方法中。

static Integer lambda$1(int offset, String s) {
    return Integer.parseInt(s) + offset;
}

4、lambda表达式相对于匿名内部来说有什么优点?

1)连接方面,上面提到的lambda工厂,这一步相当于匿名内部类的类加载过程,虽然预加热会消耗时间,但随着调用点连接的增加,代码被频繁调用后,性能会提升,另一方面,如果连接不频繁,lambda工厂方法也会比匿名内部类加载快,最高可达100倍;
2)如果lambda不用捕获变量,会自动进行优化,避免基于lambda工厂实现下额外创建对象,而匿名内部类,这一步对应的是创建外部类的实例,需要更多的内存;

5、lambda表达式是java所特有的么?

lambda表达式并非java8所特有,scala曾经通过匿名内部类的形式支持lambda表达式。

6、在android studio中使用lambda

1) 在工程的build.gradle的buildscript下的dependencies下加入依赖:

classpath 'me.tatarka:gradle-retrolambda:3.3.1'

2) 在app的build.gradle中最头上引入lambda包

apply plugin: 'me.tatarka.retrolambda'

并在android闭包标签下添加:

compileOptions {
        sourceCompatibility 1.8
        targetCompatibility 1.8
    }

这样就可以在程序中直接使用lambda快速进行开发了。

7、 lambda的一些常用替换写法

1) setOnItemClickListener的替换写法

 //之前
xxxListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
                //Do something
            }
});
//使用lambda后
xxxListView.setOnItemClickListener((parent,view,position,id)->{
    //Do something
});
//甚至
xxxListView.setOnItemClickListener((a,b,c,d)->{
    //Do something
});

2) onClickListener的写法

//之前
View.OnClickListener onClickListener = new View.OnClickListener(){ 
        @Override 
        public void onClick(View view) {
                   handleClick(); 
        }
});
findViewById(R.id.someView).setOnClickListener(onClickListener);
//使用lambda后
View.OnClickListener onClickListener = view -> handleClick();
findViewById(R.id.someView).setOnClickListener(onClickListener);

3) 遇到Rxjava中Action或Fun函数时:
如下面的请求用户信息,subscribe(Consumer

//之前               Api.getInstance().getRoleInfo(requestEnvelope)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer() {
                    @Override
                    public void accept(ResponseEnvelope responseEnvelope) throws Exception {
                        //response(responseEnvelope);
                    }
                }, new Consumer() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        //throwException(throwable);
                    }
                });
//使用lambda后                Api.getInstance().getRoleInfo(requestEnvelope)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(this::response, this::throwException));

只需在类中定义相应的response和throwException方法即可,使用上更简洁直观。

你可能感兴趣的:(Android开发)