Author: nanchen2251

Address: https://www.jianshu.com/p/c41b2fd8267a

 Kotlin Over Java

I’ve been using Kotlin for about a month, which isn’t that long, and I’m on the last leg of a wave of Google’s announcement of Kotlin as an official supported language. You might have moved from pure Java development to Android to hybrid development or even Kotlin development, but what was the reason for moving to Kotlin?

For me, it’s simple: “There’s no reason we shouldn’t use the language that Google Dad recommends!”

Kotlin has many features, such as null pointer safety, method extension, support for functional programming, rich syntactic sugar, and more. These features make Kotlin’s code much cleaner and more elegant than Java, improving code readability and maintainability, saving development time and improving development efficiency.

Of course, after using Kotlin for so long, I still have a few tips for you.

Avoid using plug-ins that come with the IDE to transform Java code

Covert Java File To Kotlin File (Covert Java File To Kotlin File) Try clicking “Convert Java File To Kotlin File” under Code in the Android Studio toolbar To see what works.

– This is the way Nam Chen initially liked to use it, without the technology but with the option of installing it in mind, writing it to a Java file, then converting it to Kotlin in one click. Even baby wants to tell you that the Kotlin branch of my 1K Star AiYaGilr project on GitHub came from this way. But there are a lot of holes to tread in.

This way is fast enough, but there will be many more!! This is due to Kotlin’s null safety feature. This is one of Kotlin’s great Android features, and I’m sure many of you have been annoyed by the NullPointException. We directly convert Java files caused by various!! , which means you may have a potentially unhandled KotlinNullPointException.

Use Val whenever possible

Val is thread-safe and doesn’t need to worry about NULL, so it’s natural to use it whenever possible.

For example, when we use Android to parse server data, we should set our data class to val because it should not be writable.

When I first used Kotlin, I thought the difference between val and var was that val stood for immutable and var stood for mutable. But the truth is more subtle than that: val doesn’t mean immutable, val means read only. This means that if you’re not allowed to explicitly declare val, it doesn’t guarantee that it’s immutable.

For ordinary variables, there is no difference between “immutable” and “read only” because you can’t overwrite a val variable, so it is immutable at this point. But in class’s member variables, the difference between read-only and immutable is big.

In Kotlin’s class, val and var are used to indicate whether a property has a getter/setter:

  • Var: has both getters and setters.

  • Val: Just the getter.

Here is a custom getter function that can return different values:

As you can see, there is no way to set the value of age, but it changes with the current date.

class Person(val birthDay: DateTime) {    val age: Int    get() = yearsBetween(birthDay, DateTime.now())}Copy the code

In this case, I recommend not customizing the getter method for the val property. If a read-only class attribute changes with certain conditions, it should be replaced with a function:

class Person(val birthDay: DateTime) {    fun age(): Int = yearsBetween(birthDay, DateTime.now())}Copy the code

This is also mentioned in Kotlin’s code convention, which uses attributes when it has the characteristics listed below, and functions when it does not:

  • No exception is thrown.

  • It has order 1 complexity.

  • The computation costs very little.

  • Multiple simultaneous calls have the same return value.

So, as mentioned above, customizing getter methods and returning different values depending on the current time violates the last rule. You should try to avoid that.

You really should pay attention to your partner

Companion objects are created in place of static members by using companion Objects in the class, similar to static inner classes in Java. So it’s common to declare constants in companion objects, but if you don’t do it right, you can incur extra overhead.

Take this code for example:

class CompanionKotlin {    companion object {        val DATA = "CompanionKotlin_DATA"    }    fun getData(): String = DATA}Copy the code

Pretty neat piece of code. But when you convert this neat Kotlin code into the Equivalent Java code, it’s hard to understand.

public final class CompanionKotlin {   @NotNull   private static final String DATA = "CompanionKotlin_DATA";   public static final CompanionKotlin.Companion Companion = new CompanionKotlin.Companion((DefaultConstructorMarker)null);   @NotNull   public final String getData() {      return DATA;   }    // ...   public static final class Companion {      @NotNull      public final String getDATA() {         return CompanionKotlin.DATA;      }      private Companion() {      }      // $FF: synthetic method      public Companion(DefaultConstructorMarker $constructor_marker) {         this();      }   }}Copy the code

Instead of Java reading a constant directly, Kotlin accesses the private constant field of an associated object through the following methods:

  • Invokes the static method of the associated object

  • Invokes the instance method of the accompanying object

  • Call the static method of the main class

  • Read static fields in the main class

Kotlin code that calls four more methods to access a constant is inefficient.

We can reduce the generated bytecode by:

  1. For primitive types and strings, constants can be declared as compile-time constants using the const keyword.

  2. For public fields, you can use the @jVMField annotation.

  3. For other types of constants, it is best to store common global constants in their own main class objects rather than in their companion objects.

@JVMStatic, @JVMFiled, Object and those kinds of stories?

We found object in Kotlin, and I was always curious about it, wondering what it was.

The object? Another object?

Someone wrote this code before, which is confusing, that a member variable of interface type accesses the member variable name of an external class. Isn’t that a given?

interface Runnable { fun run()}class Test { private val name: String = "nanchen" object impl : Runnable {override fun run() {// The compiler will raise an error. Name println(name)}}}Copy the code

Even looking at Kotlin’s official documentation, there is this description:

Sometimes we need to create an object of a slight modification of some class, without explicitly declaring a new subclass for it. Java handles this case with anonymous inner classes. Kotlin slightly generalizes this concept with object expressions and object declarations.

Kotlin uses Object instead of Java anonymous inner class implementations.

Even so, apparently, the visit should be justified. Accessing member variables from anonymous inner classes is perfectly allowed in the Java language.

It’s an interesting question, and to answer it we need to generate Java bytecode, decompile it into Java and see what the code is actually generated.

public final class Test { private final String name = "nanchen"; public static final class impl implements Runnable { public static final Test.impl INSTANCE; public void run() { } static { Test.impl var0 = new Test.impl(); INSTANCE = var0; } }}public interface Runnable { void run(); }Copy the code

Static inner class! It is true that static inner classes in Java do not allow access to member variables of external classes. But what if object replaces Java’s anonymous inner classes? So why is this static inner class?

It’s important to note here that if you just declare an object like this, Kotlin thinks you need a static inner class. If you’re using a variable to receive an object expression, Kotlin says you need an anonymous inner class object.

Therefore, the class should be improved like this:

interface Runnable {    fun run()}class Test {    private val name: String = "nanchen"    private val impl = object : Runnable {        override fun run() {            println(name)        }    }}Copy the code

To avoid this problem, keep in mind one rule: If object is only declared, it represents a static inner class. If you receive an object expression with a variable, it represents an anonymous inner class object.

There are three functions of Kotlin on object:

  • Simplify the generation of static inner classes

  • Generate an anonymous inner class object

  • Generate a singleton

@jvmStatic: @jvmField: @jvmField: @jvmField

In fact, most of our Android projects right now are a mix of Java and Kotlin, including ours. So we’re always going to be in a situation where Java and Kotlin intertune. We might often write this in code:

object Test1 {    val NAME = "nanchen"    fun getAge() = 18}Copy the code

In Java, the call would look like this:

System.out.println("name:"+Test1.INSTANCE.getNAME()+",age:"+Test1.INSTANCE.getAge());Copy the code

As a severe sufferer of OCD, I naturally cannot accept such a strange code. So I strongly recommend adding @jVMField and @jVMStatic annotations to variables and methods in Object and Companion Objects, respectively.

object Test1 {    @JvmField    val NAME = "nanchen"    @JvmStatic    fun getAge() = 18}Copy the code

This makes the outside Java calls look much better.

B: By lazy and Lateinit love each other

In Android development, we often have a number of member variables that need to be initialized in onCreate(), especially for the various controls we use in XML, and Kotlin requires that member variables be declared with an initial value by default. There will be a lot of code like this.

private var textView:TextView? = nullCopy the code

I’m under pressure not to add, okay? Represents that they can be null, and then assigns them the value NULL. In fact, we don’t want them to be empty at all in use. As a result, we have to verify that it is not null every time we use it. Such useless code is undoubtedly a waste of our working time.

Good thing Kotlin introduced the lateInit keyword: lazy loading. This way we can get around Kotlin’s mandate and use it later without having to check whether it is empty or not. But beware, access to an uninitialized causes UninitializedPropertyAccessException lateinit attribute.

And LateInit does not support basic data types, such as Int. For base data types, we can do this:

private var mNumber: Int by Delegates.notNull<Int>()Copy the code

Of course, we could also use the let function to do this, but that would be gilding the lily.

As we said, we use val as much as possible to modify variables that we know are read-only, unwritable, and immutable. Lateinit only modifies var variables, so by lazy loading, it’s time to show some real techniques.

For immutable variables, such as the parameter that the previous page passed through the bundle for the page request network, such as Presenter in MVP architecture development, we should initialize it with the by lazy keyword.

The lazy() delegate attribute can be used for lazy loading of read-only attributes, but what is often overlooked when using lazy() is that there is an optional model argument:

  • LazyThreadSafetyMode. SYNCHRONIZED: initialization properties will have double lock check, ensure the value in a single thread only computing, all threads and get the same value.

  • LazyThreadSafetyMode. PUBLICATION: multiple threads will be performed at the same time, the attributes of the initialization function will be called for many times, but only the first value is returned as the value of the entrusted property.

  • LazyThreadSafetyMode. NONE: no double lock check, should not be used in a multithreaded.

Lazy () by default will specify LazyThreadSafetyMode. SYNCHRONIZED, this may lead to unnecessary thread-safe overhead, should according to the actual situation, to specify the right model to avoid unwanted synchronization locks.

Notice the for loop in Kotlin

Kotlin provides downTo, step, until, and reversed functions to make it easier For developers to use For loops. These functions are convenient, concise, and efficient to use singly, but what about combining them? Like this:

class A {    fun loop() {        for (i in 10 downTo 0 step 3) {            println(i)        }    }}Copy the code

Using the downTo and step keywords above, let’s see how Java implements this.

public final class A { public final void loop() { IntProgression var10000 = RangesKt.step(RangesKt.downTo(10, 0), 3); int i = var10000.getFirst(); int var2 = var10000.getLast(); int var3 = var10000.getStep(); if (var3 > 0) { if (i > var2) { return; } } else if (i < var2) { return; } while(true) { System.out.println(i); if (i == var2) { return; } i += var3; }}}Copy the code

IntProgression var10000 = rangeskt.step (rangeskt.downto (10, 0), 3); In one line of code, two IntLiving temporary objects are created, adding additional overhead.

Note Kotlin’s nullability and non-nullability

Recently, there was a joke, in the project need to write a function to upload jump rope data. Hence the following code.

/ / POST("v2/rope/upload_jump_data") Observable<BaseResponse<Object>> uploadJumpData(@Field("data") List<SkipHistoryBean> data); }Copy the code

After writing the interface above, we go to the ViewModel to make the network request.

private List<SkipHistoryBean> list = new ArrayList<>(); public void uploadClick() { mNavigator.showProgressDialog(); list.add(bean); RetrofitManager.create(ISkipService.class) .uploadJumpData(list) .compose(RetrofitUtil.schedulersAndGetData()) .subscribe(new BaseSubscriber<Object>() { @Override protected void onSuccess(Object data) { mNavigator.hideProgressDialog(); mNavigator.uploadDataSuccess(); DeleteDataFromDB (); } @Override protected void onFail(ErrorBean errorBean) { super.onFail(errorBean); mNavigator.hideProgressDialog(); mNavigator.uploadDataFailed(errorBean.error_description); }}); }Copy the code

There is nothing wrong with running. But for some reason, when I changed the ISkipService class above to implement Kotlin, it crashed, and I didn’t see the problem from the code.

@post ("v2/rope/upload_jump_data") fun uploadJumpData(@field ("data") data: List<SkipHistoryBean>): Observable<BaseResponse<Any>>}Copy the code

But it did. Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: Kotlin: So the argument you need is a List that cannot be considered null. The Java code we used in our ViewModel caused a type mismatch crash because Java thought our List could be null.

The solution is as simple as allowing data to be null in the Kotlin interface or simply annotating @notnull at the call point.

Recent Articles:

  • Use the Datagrid for data paging

  • It’s time to look out for danger

  • Humor for programmers

Wait, don’t go yet! Code egg is on the move again! Participate in the activity can not only develop their own good habits, but also get “code egg” IP series exclusive prizes, speed up…

  Poke me for details   

Question of the day:

Did you catch the Kotlin train?

Message format:

Punch x days, answer: XXX.

Here’s a trick:

In just 3 steps, you won’t miss a single article!