前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AndroidStudio3 支持 Java8 了,就问你敢用吗

AndroidStudio3 支持 Java8 了,就问你敢用吗

作者头像
开发者技术前线
发布2020-11-23 15:49:46
8740
发布2020-11-23 15:49:46
举报

摘要:

今天早上,Google 发布了 AS 3.0,以及一系列的 Support 包,有意思的新东西挺多,因为之前一直在看 kotlin的支持,特地翻了一下对 Java8 的支持方式,结果……

支持 Java 8

kotlin 相关的东西很早以前我就在讲了,这里就不再细说了。AS3里面有一个亮眼的特性就是支持J8。首先说一下为什么以前我们不能用Java8的新特性,最主要的原因就是 lambda 语法。在 JVM 中,Java8 的语法是通过一个叫做invokedynamic的字节码操作命令完成的,但是这东西在 dalvik 中并没有,因此一直不能用。 现在AS3.0之所以能用,实际上是在新的Android Studio中加入了一个desugar的东西,他就类似JVM上的invokedynamic,把Java8的字节码翻译成dalvik可识别的。

官网介绍: Android Studio provides built-in support for using certain Java 8 language features and third-party libraries that use them. As shown in figure 1, the default toolchain implements the new language features by performing bytecode transformations, called desugar, on the output of the javac compiler.

desugar 能干啥

首先看张官方图:

在 javac 执行后,desugar 会对 class 做操作,将内部的lambda相关的语法转换为dalvik可识别的语法。 说的太抽象具体表现我们看代码。

代码语言:javascript
复制
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);    
    new Thread(() -> {
       Log.d("kymjs", "========");
   }).start();
}

一个这样的Java8 lambda语法的代码被编译以后,反编译它,可以看到变成了这样:

代码语言:javascript
复制
protected void onCreate(Bundle savedInstanceState) {    
   super.onCreate(savedInstanceState);    
    this.setContentView(2131296283);
   (new Thread(MainActivity$$Lambda$0.$instance)).start();
}// $FF: synthetic class

final class MainActivity$$Lambda$0 implements Runnable {   
    static final Runnable $instance = new MainActivity$$Lambda$0();    
    private MainActivity$$Lambda$0() {
   }   
   public void run() {
       MainActivity.lambda$onCreate$0$MainActivity();
   }
}

我们看到上面的代码,在编译后的run()方法内有一句MainActivity.lambdaonCreate0

上面的代码演示了纯函数(什么是纯函数自己wiki)的操作,下面看一个非纯函数的。 编译前:

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {    
    String mString = "hello";    @Override
   protected void onCreate(Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);       
       new Thread(() -> {            
          Log.d("kymjs", "========" + mString);
       }).start();
   }
}

编译后:

代码语言:javascript
复制
protected void onCreate(Bundle savedInstanceState) {   
  super.onCreate(savedInstanceState);    
   this.setContentView(2131296283);
   (new Thread(new MainActivity$$Lambda$0(this))).start();
}// $FF: synthetic class
final class MainActivity$$Lambda$0 implements Runnable {   
    private final MainActivity arg$1;   MainActivity$$Lambda$0(MainActivity var1) {        
       this.arg$1 = var1;
   }    
       public void run() {        
       this.arg$1.lambda$onCreate$0$MainActivity();
   }
}

原本的 lambda 静态对象不再是静态的了;lambda 类的构造方法多了一个外部类对象的引用。因此,如果 lambda body 不是一个非纯函数,是有可能会造成内存泄漏的(原因跟内部类持有外部类对象是一样)。 最后:附一篇官方介绍:https://developer.android.com/studio/write/java8-support.html

方法引用

这个就实在是让我哭笑不得了。官网标注,Method References 完全支持了,原本想到kotlin 的高阶函数会有性能问题,还想看看Java8会不会有这个问题。但是我用了一下,槽点满满。不管是Supplier还是Predicate,Function所有的方法调用都得要最低 API24,我靠现在普遍都是兼容到14的吧,你这让我怎么用高阶方法。不过我也尝试不考虑低版本写了一个,看了一下效果。

首先是Java8编译前代码:

代码语言:javascript
复制
public String str = "hello";
 @Override
protected void onCreate(Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);   test(() -> str);
}public void test(Supplier<String> block) {
   System.out.println("=======" + block.get());
}

Java8编译后,貌似换汤不换药,只替换lambda部分,方法内依旧是普通对象方法调用:

代码语言:javascript
复制
protected void onCreate(Bundle savedInstanceState) {    
super.onCreate(savedInstanceState);    
this.setContentView(2131296283);    
this.test(new MainActivity$$Lambda$0(this));
}
public void test(Supplier<String> block) {
   System.out.println("==========" + (String)block.get());
}// $FF: synthetic class

final class MainActivity$$Lambda$0 implements Supplier {    
    private final MainActivity arg$1;
   MainActivity$$Lambda$0(MainActivity var1) {       
       this.arg$1 = var1;
   }   
    public Object get() {        
        return this.arg$1.lambda$onCreate$0$MainActivity();
   }
}

kotlin编译前代码:

代码语言:javascript
复制
val str: String = "hello"
override fun onCreate(savedInstanceState: Bundle?) {    
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)
   test {
       str
   }
}fun test(block: () -> String) {
   println("=========${block.invoke()}")
}

kotlin编译后的代码:

代码语言:javascript
复制
protected void onCreate(@Nullable Bundle savedInstanceState) {     super.onCreate(savedInstanceState);
   setContentView((int) R.layout.activity_main);
   test(new MainActivity$onCreate$1(this));
}public final void test(@NotNull Function0<String> block) {   
 Intrinsics.checkParameterIsNotNull(block, "block");    
 System.out.println("=========" + ((String) block.invoke()));
}/* compiled from: MainActivity.kt */

final class MainActivity$onCreate$1 extends Lambda implements Function0<String> {    
 final /* synthetic */ MainActivity this$0;    
 MainActivity$onCreate$1(MainActivity mainActivity) {        
      this.this$0 = mainActivity;        
      super(0);
   }    
    @NotNull
   public final String invoke() {        
      return this.this$0.getStr();
   }
}

所以说,kotlin在实现上跟Java也依旧是一模一样,首先生成一个类,把lambda转换成对象,再调用这个对象的invoke()方法。但是别忘了,kotlin有神奇的inline关键字,就专门用来解决这种莫名其妙的多生成一大堆对象的情况。 假设给上面的test()方法加上inline关键字后,编译后的代码就变成了这样,相当于并没有调用test()方法,因此也就不存在多生成的Function0对象了:

代码语言:javascript
复制
protected void onCreate(
  @Nullable Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);
   setContentView((int) R.layout.activity_main);
   MainActivity this_$iv = this;
   System.out.println("=========" + this.str);
}
public final void test(@NotNull Function0<String> block) {
   Intrinsics.checkParameterIsNotNull(block, "block");
   System.out.println("=========" + ((String) block.invoke()));
}

原本我是想夸一夸J8的支持,因为我最初以为他是将 lambda body 的纯函数转换成静态方法,直接将 lambda 改成静态方法调用来做的,结果没想到还不如 kotlin。感觉就是 Google 为了 KPI 去加了一个 Java8 支持的噱头。

本文开源实验室原创 本文地址:https://kymjs.com/code/2017/10/26/01

相关推荐

再探Java内存分配

Kotlin VS Java 编译速度大比拼,到底谁更快?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-10-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发者技术前线 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档