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:
-
For primitive types and strings, constants can be declared as compile-time constants using the const keyword.
-
For public fields, you can use the @jVMField annotation.
-
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? = null
Copy 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!