Perform steadily conversion during the 10 consecutive holidays I made my own Java Android application (about 300 files) into full Kotlin.
↓ Kotlin 100% diagram
Kolin First was announced at Google I / O 2019, which was held just after the 10th consecutive holidays. I'm going to have more opportunities to convert from Java to Kotlin, so I'll summarize what I stumbled upon and how to avoid it.
The environment is Android Studio 3.4.
You can be sure that everything is done manually, but Android Studio provides conversion commands. "Menu> Code> Convert Java File to Kotlin File" The shortcut is "Shift + Option + Command + k".
The comments sometimes hindered the conversion.
The commented out part remains Java after Kotlin conversion.
Also, Kotlin requires that you have the same number of / *
to open comments and * /
to close comments.
[Sample before conversion]
[Sample after conversion]
--The comment part remains Java --Insufficient number of comments to close Build error
Kotlin is a NullSafe language specification, so if Nullable / NonNull is ambiguous on the Java side, It is treated as a platform type (a type that may be Nullable or NonNull). It is safer to treat it as Nullable unless NonNull is deterministic.
The code that actually caused a runtime error is below.
Those with !
After the variable are platform type and can be treated as NonNull in the code.
Null may come in at runtime and an error may occur.
Once it is treated as Nullable and early return etc., it can be treated as NonNull from the next line.
For Nullable / NonNull, when using Kotlin conversion command In particular, the following exceptions were likely to occur.
Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null:
This is an exception that occurs when the superclass is Java and the subclass is Kotlin and the non-null and null are mishandled. For example, an exception will occur in the following cases that inherit AppCompatActivity. It's a run-time error, so the code builds successfully.
class MainActivity : AppCompatActivity() {
:
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
:
The correct code is to have Bundle?
As the argument type, as shown below.
class MainActivity : AppCompatActivity() {
:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
:
This is hard to find without reading the code on the superclass side.
When I read the actual code, the Bundle is annotated with @Nullable
.
You can see that Bundle?
Is correct instead of Bundle
.
androidx-appcompat:[email protected]
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
Based on the above precautions, here are some things you should do before conversion.
Both Kotlin official and Android official have interoperability documentation with Java.
It's better to distinguish NonNull as much as possible at the time of Java. According to the Kotlin documentation, the following annotations are supported.
https://kotlinlang.org/docs/reference/java-interop.html
JetBrains (@Nullable and @NotNull from the org.jetbrains.annotations package) Android (com.android.annotations and android.support.annotations) JSR-305 (javax.annotation, more details below) FindBugs (edu.umd.cs.findbugs.annotations) Eclipse (org.eclipse.jdt.annotation) Lombok (lombok.NonNull).
If you are using the old support library, it is better to convert after migrating to Android X. Android X has less risk of conversion to Kotlin because NonNull and Nullable are clearly discriminated. Below is a comparison of Java code for RecyclerView.
Old support library RecyclerView https://android.googlesource.com/platform/frameworks/support/+/121ba96/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java#5143
public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
RecyclerView in AndroidX library https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java#6888
@NonNull
public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
If you add a unit test at the time of Java, it is easy to check the operation after Kotlin conversion. It is recommended to add a unit test as it is the minimum.
If you search for "interop" etc. in Editor> Inspection, the interoperable Lint that can be used for Kotlin conversion will be displayed. If checked, dangerous code will be warned before and after conversion.
https://developer.android.com/kotlin/interop#lint_checks https://developer.android.com/studio/write/lint?hl=ja
Kotlin reserved words such as is, object, when, and get, set, etc. used in the internal Java code should be renamed in advance. You can use Kotlin by enclosing it in backticks, but you should avoid it.
The Kotlin code generated by the Kotlin conversion command is hard to say that it utilizes Kotlin. At the very least, here's how to make it safe to some extent without warning.
The code after the conversion command basically uses !!
to unwrap it into NonNull.
Consider whether it can be safely unwrapped with ? .
etc.
Code example immediately after Kotlin conversion
var webView: WebView? = null
fun load() {
if (webView != null) {
webView!!.load()
}
:
Example of utilizing ?.
var webView: WebView? = null
fun load() {
webView?.load()
:
Consider whether Nullable properties can be used, such as lateinit
and by lazy
.
If you want to initialize other than ʻinit such as ʻActivity # onCreate
related to View, you can use lateinit
to handle it as a property of NonNull.
private var mRecyclerView: RecyclerView? = null
override fun onCreate(savedInstanceState: Bundle?) {
:
mRecyclerView = findViewById(R.id.recycler_view)
private lateinit var mRecyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
:
mRecyclerView = findViewById(R.id.recycler_view)
In Java, static final constants are treated as val in the code after the Kotlin conversion command. Consider whether it can be const val (usually, if you follow Lint, you will add const)
companion object {
private val TAG = "MainActivity"
companion object {
private const val TAG = "MainActivity"
After conversion, even if you think you wrote the test code properly, there may be bugs that are beyond your expectations. This can be prevented by having a third party or Robo test it. There are more types such as Nullable / NonNull / Platform, and more states such as lateinit before initialization. We recommend using the Firebase Robo Test to prevent "inadvertent mistakes".
↑ Run-time error found in my Robo Test. A mistake that accessed lateinit before initialization.Kotlin itself is refreshing, but errors tend to occur when bridging Kotlin ⇔ Java. It's a pretty thorny road, so it's better to have Java code that is safe in advance. Once converted, you can use useful functions such as Coroutine and extension functions to change your life.
Recommended Posts