Kotlin 与 Java 代码之间的转换

Java 代码转换成 Kotlin 代码

有许多老项目是使用 Java 语言编写的,而现在想要转换成 Kotlin 语言,最笨的转换方式就是对每一行代码都重新手动编写。

第一种转换方式

事实上,将 Java 代码转换成 Kotlin 代码,在语法层面上是有一定规律的,而 Android Studio 给我们提供了非常便利的功能来一键完成这种转换工作。

首先,只要复制一段 Java 代码,然后在 Android Studio 中的任意一个 Kotlin 文件中进行粘贴,Android Studio 都会弹出提示框,询问我们是否要把即将粘贴的 Java 代码片段转换成 kotlin 代码。

但上述这种实现方式,它只会按照固定的语法变化规律来执行转换工作,而不会自动应用 Kotlin 的各种优秀特性。因此,依靠这种自动转换工具只能实现基础版的 Kotlin 语法,细节方面的代码优化还是得靠我们手动完成。

第二种转换方式

除了复制粘贴的方式外,还可以直接将一个 Java 文件以及其中的所有代码一次性转换成 Kotlin 版本。具操操作是:首先打开 Java 文件,然后点击导航栏中的 Code -> Convert Java File to Kotlin File。


Kotlin 代码转换成 Java 代码

Android Studio 并没有提供类似的功能,因为 Kotlin 拥有许多 Java 中并不存在的特性,因此很难执行这样的一键转换。

但是,却可以先将 Kotlin 代码转换成 Kotlin 字节码,然后再通过反编译的方式将它还原成 Java 代码。这种反编译出来的代码可能无法像正常编写的 Java 代码那样直接运行,但是非常有利于帮助理解诸多 Kotlin 特性背后的实现原理。

示例(Kotlin):kotlin-android-extensions 插件原理

1
2
3
4
5
6
7
8
9
10
11
12
13
class MainActivity : BaseActivity(){

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// 'kotlin-android-extensions' 插件的作用,不必再 findViewById()。
// 这个插件会根据布局文件中定义的控件 id 自动生成一个具有相同名称的变量。
btn_main.setOnClickListener {
Toast.makeText(this,"Toast",Toast.LENGTH_SHORT).show()
}
}
}

首先,点击 Android Studio 导航栏中的 Tools -> Kotlin -> Show Kotlin Bytecode,此时会弹出窗口并显示这段 Kotlin 代码的字节码。

然后,点击窗口中左上角的 ”Decompile“ 按钮,就可以将这些 Kotlin 字节码反编译成 Java 代码。通过如下这段代码,就可大致分析出 kotlin-android-extensions 插件背后的实现原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Metadata(
mv = {1, 1, 15},
bv = {1, 0, 3},
k = 1,
d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001a\u00020\u00042\b\u0010\u0005\u001a\u0004\u0018\u00010\u0006H\u0014¨\u0006\u0007"},
d2 = {"Lcom/example/myapplication/activity/MainActivity;", "Lcom/example/myapplication/base/BaseActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "app_debug"}
)
public final class MainActivity extends BaseActivity {
private HashMap _$_findViewCache;

protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(-1300033);
// 它会在 Activity 中自动生成一个 _$_findCachedViewById(),并通过方法获得 button 按钮的实例,再调用 setOnClickListener() 对按钮的点击事件进行注册。
((Button)this._$_findCachedViewById(id.btn_main)).setOnClickListener((OnClickListener)(new OnClickListener() {
public final void onClick(View it) {
Toast.makeText((Context)MainActivity.this, (CharSequence)"Toast", 0).show();
}
}));
}

// 在这个方法中根据传入的 id 调用 findViewById() 来查询并获取控件的实例,然后使用 HashMap 对该实例进行缓存,这样下次就没必要重复进行查询了。
public View _$_findCachedViewById(int var1) {
if (this._$_findViewCache == null) {
this._$_findViewCache = new HashMap();
}

View var2 = (View)this._$_findViewCache.get(var1);
if (var2 == null) {
var2 = this.findViewById(var1);
this._$_findViewCache.put(var1, var2);
}

return var2;
}

public void _$_clearFindViewByIdCache() {
if (this._$_findViewCache != null) {
this._$_findViewCache.clear();
}

}
}

备注

参考资料

第一行代码(第3版)