The articles
Kotlin Jetpack: The Beginning
00. The Kotlin Pit Guide for Java Developers
01. Start with a Demo of worshiping a God
02. What is the Experience of Writing Gradle scripts with Kotlin?
Introduction to the
This article assumes that you already have the foundation of Kotlin. If you are not familiar with Kotlin, you can read my previous article.
This article will walk you through the step-by-step reconstruction of our Demo project with Kotlin and take a look at the triple realm of Kotlin programming.
Note: This series of articles deals only with Kotlin JVMS, not Kotlin JS/Native.
The main content
preparation
The first stage: Write Kotlin from a Java perspective
The second realm: Write Kotlin from Kotlin’s perspective
Third level: Write Kotlin from the Bytecode perspective
At the end
preparation
- Update the Android Studio version to the latest
- Clone our Demo project locally and open it with Android Studio:
Github.com/chaxiu/Kotl…
- Switch to branch:
chapter_03_kotlin_refactor_training
- I strongly suggest that you follow this article to practice, the actual combat is the essence of this article
Add Kotlin support to the project
We changed Groovy to a Kotlin DSL in the previous chapter, but the project itself doesn’t support writing Android apps in Kotlin. So we also need to do some configuration:
Libs.kt adds the following dependency constants:
const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${Versions.kotlinVersion}"
const val ktxCore = "androidx.core:core-ktx:${Versions.ktxCore}"
Copy the code
Build.gradle. kt added in the root directory:
dependencies {
...
classpath(kotlin("gradle-plugin", version = Versions.kotlinVersion))
}
Copy the code
App/build. Gradle. Kt feature:
plugins {
...
kotlin("android")
kotlin("android.extensions")
}
dependencies {
...
implementation(Libs.kotlinStdLib)
implementation(Libs.ktxCore)
}
Copy the code
Note: The above configuration is sufficient for pure Kotlin development, but if you have Java hybrid development, it is best to add the following compiler parameters to prevent compatibility problems: app/build.gradle.kt
android {
...
// Configure Java compiler compatible with Java 1.8
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
// Configure Kotlin compiler target Java 1.8 when compile Kotlin to bytecode
kotlinOptions {
this as KotlinJvmOptions
jvmTarget = "1.8"}}Copy the code
The functions of the above configurations are as follows:
- Configure the Java compiler to be compatible with Java 1.8
- Configure the Kotlin compiler to generate bytecode with the Java 1.8 specification
See my GitHub Commit for details on the above changes.
Let’s get down to business, refactoring Java code with Kotlin.
The body of the
I’ve always thought of Kotlin as an easy language to learn but hard to master: easy to get started, hard to master. If there are three realms for Kotlin programmers, I think there are three realms.
1. The first stage: Write Kotlin from a Java perspective
This is something that almost every Kotlin programmer experiences (including me). I thought I could write Kotlin code by learning Kotlin syntax, but I just translated the Java/C code in my head into Kotlin syntax.
I’m going to rebuild our Demo project using the “skills” of the first realm. Just watch the fun, don’t take it into your head. [head]
I’m pretending I’m new to Kotlin grammar. Is the so-called, persimmon to pick soft pinch, let’s rebuild the code from the simplest of course. So I found the Demo project user.java, a bite of teeth, you:
public class User {
// The project is so simple that there is no database, so the API requests are cached here
public static final String CACHE_RESPONSE = "{"login":"JakeWharton","id": 66577,"node_id":"MDQ6VXNlcjY2NTc3","avatar_url":"https://avatars0.githubusercontent.com/u/66577?v=4","gravatar_id":"","url":"https://api.github.com/users/JakeWharton","html_ur L ":" https://github.com/JakeWharton ", "followers_url" : "https://api.github.com/users/JakeWharton/followers," friend "following_u rl":"https://api.github.com/users/JakeWharton/following{/other_user}","gists_url":"https://api.github.com/users/JakeWhar ton/gists{/gist_id}","starred_url":"https://api.github.com/users/JakeWharton/starred{/owner}{/repo}","subscriptions_url" :"https://api.github.com/users/JakeWharton/subscriptions","organizations_url":"https://api.github.com/users/JakeWharton/ orgs","repos_url":"https://api.github.com/users/JakeWharton/repos","events_url":"https://api.github.com/users/JakeWharto n/events{/privacy}","received_events_url":"https://api.github.com/users/JakeWharton/received_events","type":"User","site _admin":false,"name":"Jake Wharton","company":"Square","blog":"https://jakewharton.com","location":"Pittsburgh, PA, USA","email":null,"hireable":null,"bio":null,"twitter_username":null,"public_repos":104,"public_gists":54,"followers":57 849,"following":12,"created_at":"2009-03-24T16:09:53Z","updated_at":"2020-05-28T00:07:20Z"}";
private String id;
private String login;
private String avatar_url;
private String name;
private String company;
private String blog;
private Date lastRefresh;
public User(a) {}public User(@NonNull String id, String login, String avatar_url, String name, String company, String blog, Date lastRefresh) {
this.id = id;
this.login = login;
this.avatar_url = avatar_url;
this.name = name;
this.company = company;
this.blog = blog;
this.lastRefresh = lastRefresh;
}
public String getId(a) { return id; }
public String getAvatar_url(a) { return avatar_url; }
public Date getLastRefresh(a) { return lastRefresh; }
public String getLogin(a) { return login; }
public String getName(a) { return name; }
public String getCompany(a) { return company; }
public String getBlog(a) { return blog; }
public void setId(String id) { this.id = id; }
public void setAvatar_url(String avatar_url) { this.avatar_url = avatar_url; }
public void setLastRefresh(Date lastRefresh) { this.lastRefresh = lastRefresh; }
public void setLogin(String login) { this.login = login; }
public void setName(String name) { this.name = name; }
public void setCompany(String company) { this.company = company; }
public void setBlog(String blog) { this.blog = blog; }
Copy the code
In a quick operation, I translated the Java Bean using Kotlin syntax like this:
class User {
companion object {
val CACHE_RESPONSE = "..."
}
private var id: String? = null
private var login: String? = null
private var avatar_url: String? = null
private var name: String? = null
private var company: String? = null
private var blog: String? = null
private var lastRefresh: Date? = null
constructor() {}
constructor(id: String, login: String? , avatar_url: String? , name: String? , company: String? , blog: String? , lastRefresh: Date?) {this.id = id
this.login = login
this.avatar_url = avatar_url
this.name = name
this.company = company
this.blog = blog
this.lastRefresh = lastRefresh
}
fun getId(a): String? { return id }
fun getAvatar_url(a): String? { return avatar_url }
fun getLastRefresh(a): Date? { return lastRefresh }
fun getLogin(a): String? { return login }
fun getName(a): String? { return name }
fun getCompany(a): String? { return company }
fun getBlog(a): String? { return blog }
fun setId(id: String?). { this.id = id }
fun setAvatar_url(avatar_url: String?). { this.avatar_url = avatar_url }
fun setLastRefresh(lastRefresh: Date?). { this.lastRefresh = lastRefresh }
fun setLogin(login: String?). { this.login = login }
fun setName(name: String?). { this.name = name }
fun setCompany(company: String?). { this.company = company }
fun setBlog(blog: String?). { this.blog = blog }
}
Copy the code
I looked at my Kotlin code line by line and felt a sense of accomplishment. So easy! [head]
To enable the project to emulate the Kotlin/Java mashup, we left ImagePreviewActivity in the Java state, so the mainActivity.java refactoring was left. Let’s start with the Java code for MainActivity.
public class MainActivity extends AppCompatActivity {
public static final String TAG = "Main";
public static final String EXTRA_PHOTO = "photo";
StringRequest stringRequest;
RequestQueue requestQueue;
private ImageView image;
private ImageView gif;
private TextView username;
private TextView company;
private TextView website;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init(a) {
image = findViewById(R.id.image);
gif = findViewById(R.id.gif);
username = findViewById(R.id.username);
company = findViewById(R.id.company);
website = findViewById(R.id.website);
display(User.CACHE_RESPONSE);
requestOnlineInfo();
}
private void requestOnlineInfo(a) {
requestQueue = Volley.newRequestQueue(this);
String url ="https://api.github.com/users/JakeWharton";
stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) { display(response); }},new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_SHORT).show(); }}); stringRequest.setTag(TAG); requestQueue.add(stringRequest); }private void display(@Nullable String response) {
if (TextUtils.isEmpty(response)) { return; }
Gson gson = new Gson();
final User user = gson.fromJson(response, User.class);
if(user ! =null){
Glide.with(this).load("file:///android_asset/bless.gif").into(gif);
Glide.with(this).load(user.getAvatar_url()).apply(RequestOptions.circleCropTransform()).into(image);
this.username.setText(user.getName());
this.company.setText(user.getCompany());
this.website.setText(user.getBlog());
image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { gotoImagePreviewActivity(user); }}); }}private void gotoImagePreviewActivity(User user) {
Intent intent = new Intent(this, ImagePreviewActivity.class);
intent.putExtra(EXTRA_PHOTO, user.getAvatar_url());
startActivity(intent);
}
@Override
protected void onStop (a) {
super.onStop();
if(requestQueue ! =null) { requestQueue.cancelAll(TAG); }}}Copy the code
With one operation, I refactor MainActivity to look like this:
class MainActivity : AppCompatActivity() {
companion object {
val TAG = "Main"
val EXTRA_PHOTO = "photo"
}
var stringRequest: StringRequest? = null
var requestQueue: RequestQueue? = null
private var image: ImageView? = null
private var gif: ImageView? = null
private var username: TextView? = null
private var company: TextView? = null
private var website: TextView? = null
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()}private fun init(a) {
image = findViewById(R.id.image)
gif = findViewById(R.id.gif)
username = findViewById(R.id.username)
company = findViewById(R.id.company)
website = findViewById(R.id.website)
display(User.CACHE_RESPONSE)
requestOnlineInfo()
}
private fun requestOnlineInfo(a) {
requestQueue = Volley.newRequestQueue(this)
val url = "https://api.github.com/users/JakeWharton"
stringRequest = StringRequest(Request.Method.GET, url,
object: Response.Listener<String> {
override fun onResponse(response: String?). {
display(response)
}
}, object: Response.ErrorListener {
override fun onErrorResponse(error: VolleyError?). {
Toast.makeText(this@MainActivity, error? .message, Toast.LENGTH_SHORT).show() } }) stringRequest!! .tag = TAG requestQueue!! .add(stringRequest) }private fun display(response: String?). {
if (TextUtils.isEmpty(response)) {
return
}
val gson = Gson()
val user = gson.fromJson(response, User::class.java)
if(user ! =null) {
Glide.with(this).load("file:///android_asset/bless.gif").into(gif!!)
Glide.with(this).load(user.getAvatar_url()).apply(RequestOptions.circleCropTransform()).into(image!!) username!! .text = user.getName() company!! .text = user.getCompany() website!! .text = user.getBlog() image!! .setOnClickListener(object: View.OnClickListener{
override fun onClick(v: View?). {
gotoImagePreviewActivity(user)
}
})
}
}
private fun gotoImagePreviewActivity(user: User) {
val intent = Intent(this, ImagePreviewActivity::class.java)
intent.putExtra(EXTRA_PHOTO, user.getAvatar_url())
startActivity(intent)
}
override fun onStop(a) {
super.onStop()
if(requestQueue ! =null) { requestQueue!! .cancelAll(TAG) } } }Copy the code
Due to MainActivity reconstruction became Kotlin, ImagePreviewActivity. Java need corresponding to do some adjustments. The reason is that Java does not recognize associated objects very well.
Modify before:
public class ImagePreviewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); . String url = intent.getStringExtra(MainActivity.EXTRA_PHOTO); . }}Copy the code
Revised:
public class ImagePreviewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); . String url = intent.getStringExtra(MainActivity.Companion.getEXTRA_PHOTO()); . }}Copy the code
summary
This realm is characterized by a line of Kotlin corresponding to a line of Java, without using kotlin-specific features.
See my GitHub Commit for details on the above changes.
Dear friends do not see here to walk ah, look at my next realm is how to write (play).
2. The second realm: Write Kotlin from the perspective of Kotlin
At the second level, I’m a full-fledged Kotlin programmer. I’ll use some Kotlin specific features to improve the logic in Java code.
2-1 Data Class
Let’s start with the simplest User. Kt file. If you have read Kotlin’s Guide to Java Developers, you must know the Data Class.
data class User(
var id: String? = null.var login: String? = null.var avatar_url: String? = null.var name: String? = null.var company: String? = null.var blog: String? = null.var lastRefresh: Date? = null
) {
companion object {
val CACHE_RESPONSE = "..."}}Copy the code
summary
The Data Class saves us time writing Java beans.
2-2 lateinit
Next, mainActivity.kt, we start with the top variable. All of the variables we defined were Nullable, resulting in Nullable or non-null assertions being used. . That’s not Kotlin. There are many ways to solve this problem, but here I’ll start with LateInit to solve two variables of the network request.
Modify before:
class MainActivity : AppCompatActivity() {...var stringRequest: StringRequest? = null
var requestQueue: RequestQueue? = null
private fun requestOnlineInfo(a){... stringRequest!! .tag = TAG requestQueue!! .add(stringRequest) } }Copy the code
Revised:
class MainActivity : AppCompatActivity() {...private lateinit var stringRequest: StringRequest
private lateinit var requestQueue: RequestQueue
private fun requestOnlineInfo(a){... stringRequest.tag = TAG requestQueue.add(stringRequest) } }Copy the code
summary
In general, variables that are not null need to be assigned in the constructor or init block so that the compiler doesn’t get an error. But a lot of times our variable assignments don’t work in these cases, like findViewById.
Lateinit is telling the compiler that this variable THAT I’ve defined that is not empty, I haven’t assigned it yet, but before I use it, I’m going to assign it, it’s definitely not empty, you don’t have to get an error.
2-3 Kotlin-Android-Extensions
KTX is a Gradle plugin for Android that provides convenience to developers. It is best known for its ability to dispense with findViewById. We have already added this plug-in in the project, so we can use it directly.
Delete the declaration and assignment of the control directly, and then press Option + return to select import from the call:
Modify before:
private var image: ImageView? = null
private var gif: ImageView? = null
private var username: TextView? = null
private var company: TextView? = null
private var website: TextView? = nullimage = findViewById(R.id.image) gif = findViewById(R.id.gif) username = findViewById(R.id.username) company = findViewById(R.id.company) website = findViewById(R.id.website) ... username!! .text = user.name company!! .text = user.company website!! .text = user.blogCopy the code
Revised:
// Notice here
import kotlinx.android.synthetic.main.activity_main.*
// private var image: ImageView? = null
// private var gif: ImageView? = null
// private var username: TextView? = null
// private var company: TextView? = null
// private var website: TextView? = null
// image = findViewById(R.id.image)
// gif = findViewById(R.id.gif)
// username = findViewById(R.id.username)
// company = findViewById(R.id.company)
// website = findViewById(R.id.website). username.text = user.name company.text = user.company website.text = user.blogCopy the code
summary
- KTX certainly provides more convenience than just replacing findViewById, which we’ll talk about later
- While KTX offers convenience, there are certain things
Hidden trouble
We’ll talk about that later
2-4 Lambda
The following code will prompt Android Studio to Convert to lambda. We just need to press Option + Return and Android Studio will help us refactor.
Modify before:
. stringRequest = StringRequest(Request.Method.GET, url,object : Response.Listener<String> {
override fun onResponse(response: String?). {
display(response)
}
}, object : Response.ErrorListener {
override fun onErrorResponse(error: VolleyError?). {
Toast.makeText(this@MainActivity, error? .message, Toast.LENGTH_SHORT).show() } }) ... image.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?). {
gotoImagePreviewActivity(user)
}
})
Copy the code
Revised:
. stringRequest = StringRequest(Request.Method.GET, url, Response.Listener { response -> display(response) }, Response.ErrorListener { error -> Toast.makeText(this@MainActivity, error? .message, Toast.LENGTH_SHORT).show() }) ... image.setOnClickListener { gotoImagePreviewActivity(user) } ...Copy the code
summary
- How to use Kotlin Lambda for the moment
- The biggest benefit of using Lambda as an interface implementation here is that it actually improves the code
readability
2-5 Extension functions
Use Kotlin’s extension functions to wipe out any xxutils.java. The Kotlin standard functions already provide extension functions to help eliminate TextUtils.
Modify before:
.if (TextUtils.isEmpty(response)) {
return
}
Copy the code
Revised:
.if (response.isNullOrBlank()) {
return
}
Copy the code
The modified code above looks like response has a member method: isNullOrBlank(), which has a number of benefits:
- Writing code is smoother, and the IDE will automatically indicate which methods a class can call instead of looking for xxUtils
- Better code readability
2-6 Standard functions apply
Kotlin provides a series of standard functions such as let, Also, with, and apply to help developers simplify their logic. Here we use apply, which is cumbersome to explain and makes the code clearer:
Modify before:
if(user ! =null) {... username.text = user.name website.text = user.blog image.setOnClickListener { gotoImagePreviewActivity(user) } }Copy the code
Revised:
user? .apply { ... username.text = name website.text = blog image.setOnClickListener { gotoImagePreviewActivity(this)}}Copy the code
summary
This realm is characterized by:
- One line of Kotlin code can correspond to multiple lines of Java code
- Code readability enhancement
- Better code robustness
See the Github Commit for details.
Third level: Write Kotlin from the Bytecode perspective
Kotlin claims Java is 100% compatible because Kotlin will eventually be compiled into Bytecode. By looking at Kotlin’s compiled bytecodes, we can both understand how Kotlin works and explore some of Kotlin’s programming Tips.
Due to the limited space of this article, we won’t discuss how Kotlin is implemented or explore Kotlin programming Tips in detail. We continue to focus on the field. In the current project, we have tried to add some Kotlin features, and we will only study those Kotlin features that are used at this stage.
3-1 How do I view the bytecode corresponding to Kotlin?
Tools -> Kotlin -> Show Kotlin Bytecode -> Show Kotlin Bytecode -> Show Kotlin Bytecode -> Show Kotlin Bytecode So you can see Kotlin’s equivalent Java code.
3-2 Eliminate Mutability whenever possible
Variables modified by final in Java cannot be modified once assigned. This is also a good habit in Java and should be used in Kotlin. Kotlin doesn’t have a final, but it has a val.
Let’s start with User.kt.
data class User(
var id: String? = null.var login: String? = null.var avatar_url: String? = null.var name: String? = null.var company: String? = null.var blog: String? = null.var lastRefresh: Date? = null
) {
companion object {
val CACHE_RESPONSE = "..."}}Copy the code
User.kt decompiled into Java:
.public final class User {
@Nullable
privateString id; .@NotNull
private static final String CACHE_RESPONSE = "...";
public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null);
@Nullable
public final String getId(a) {
return this.id;
}
public final void setId(@Nullable String var1) {
this.id = var1; }...public static final class Companion {
@NotNull
public final String getCACHE_RESPONSE(a) {
return User.CACHE_RESPONSE;
}
private Companion(a) {}// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this(a); }}}Copy the code
Let’s replace all var in user.kt with val:
data class User(
val id: String? = null.val login: String? = null.val avatar_url: String? = null.val name: String? = null.val company: String? = null.val blog: String? = null.val lastRefresh: Date? = null
) {
companion object {
val CACHE_RESPONSE = "..."}}Copy the code
It decompiled into Java code to look like this:
public final class User {
@Nullable
private final String id; / / the final.@NotNull
private static final String CACHE_RESPONSE = "...";
public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null);
@Nullable
public final String getId(a) {
return this.id;
}
// setId() is not available.public static final class Companion {
@NotNull
public final String getCACHE_RESPONSE(a) {
return User.CACHE_RESPONSE;
}
private Companion(a) {}// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this(a); }}}Copy the code
Summary:
- Kotlin is based on the JVM, so previous Java programming experience is also useful
- When we change the var of a Data Class to val, its member variables are final, and the set method is gone. Once a Data Class is instantiated, it cannot be modified
- What if you want to modify a Data Class member variable? With the copy method
Minimize the Scope of variables
This is also useful in Java and Kotlin. There are two member variables in mainActivity. kt where stringRequest can actually be changed to a local variable.
Modify before:
class MainActivity : AppCompatActivity() {...private lateinit var stringRequest: StringRequest
private lateinit var requestQueue: RequestQueue
}
Copy the code
Revised:
class MainActivity : AppCompatActivity() {...// private lateinit var stringRequest: StringRequest
private lateinit var requestQueue: RequestQueue
private fun requestOnlineInfo(a){...val stringRequest = StringRequest(Request.Method.GET,
url,
Response.Listener { response ->
display(response)
},
Response.ErrorListener { error ->
Toast.makeText(this@MainActivity, error?.message, Toast.LENGTH_SHORT).show()
})
...
}
}
Copy the code
3. By lazy
MainActivity only has one member variable left, requestQueue, which is decorated with var. Can we change it to val? Sure, but we need by lazy delegate.
Revised:
class MainActivity : AppCompatActivity() {...private val requestQueue: RequestQueue by lazy {
Volley.newRequestQueue(this)}}Copy the code
Let’s look at its equivalent Java code, which is initialized to lazykt.lazy:
private final Lazy requestQueue$delegate = LazyKt.lazy((Function0)(new Function0() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(a) {
return this.invoke();
}
public final RequestQueue invoke(a) {
return Volley.newRequestQueue((Context)MainActivity.this); }}));Copy the code
Consider the implementation of lazykt. lazy, which is SynchronizedLazyImpl:
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
Copy the code
Look again at SynchronizedLazyImpl:
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private vallock = lock ? :this
override val value: T
get() {
val _v1 = _value
if(_v1 ! == UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if(_v2 ! == UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
valtypedValue = initializer!! () _value = typedValue initializer =null
typedValue
}
}
}
override fun isInitialized(a): Boolean= _value ! == UNINITIALIZED_VALUEoverride fun toString(a): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(a): Any = InitializedLazyImpl(value)
}
Copy the code
Sure enough, as mentioned in our previous article, by Lazy is initialized synchronously by default. This is not necessary for our current project, as multithreaded synchronization is also expensive.
Revised:
private val requestQueue: RequestQueue by lazy(LazyThreadSafetyMode.NONE) {
Volley.newRequestQueue(this)}Copy the code
3-5 Do not use the wrong companion
Since Java does not recognize the associated objects in Kotlin, it is awkward to access them in Java.
class MainActivity : AppCompatActivity() {
companion object{...val EXTRA_PHOTO = "photo"}}Copy the code
Access in Java:
public class ImagePreviewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {... String url = intent.getStringExtra(MainActivity.Companion.getEXTRA_PHOTO()); . }}Copy the code
After decompiling:
.@NotNull
private static final String EXTRA_PHOTO = "photo";
public static final MainActivity.Companion Companion = new MainActivity.Companion((DefaultConstructorMarker)null); .public static final class Companion {
@NotNull
public final String getEXTRA_PHOTO(a) {
return MainActivity.EXTRA_PHOTO;
}
private Companion(a) {}// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
Copy the code
We can see that, by default, the Kotlin generated the get method for associated variables in the object, the Java code to access the variable must be like this: MainActivity.Com panion. GetEXTRA_PHOTO (), it is not very friendly.
To enable Java to better recognize variables and methods in associated objects, we can do this:
Use the const:
class MainActivity : AppCompatActivity() {
companion object{...const val EXTRA_PHOTO = "photo"}}Copy the code
Or use the @jVMField annotation:
class MainActivity : AppCompatActivity() {
companion object{...@JvmField
val EXTRA_PHOTO = "photo"}}Copy the code
Access in Java:
public class ImagePreviewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {... String url = intent.getStringExtra(MainActivity.EXTRA_PHOTO); . }}Copy the code
The decompiled Java code in both cases looks like this:
.@NotNull
public static final String EXTRA_PHOTO = "photo";
public static final MainActivity.Companion Companion = new MainActivity.Companion((DefaultConstructorMarker)null); .public static final class Companion {
@NotNull
public final String getTAG(a) {
return MainActivity.TAG;
}
private Companion(a) {}// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
Copy the code
Many bloggers say that’s where the companion ends. @jVMField, const, @jVMStatic, those are really the things to be careful about when you’re using associated objects.
But isn’t our code perfect at this point? Don’t.
We can see that even if we add @jVMField or const, the Companion object still generates get methods for constants, and defines a Companion class, as well as an instance. However, our initial requirement was simply to define a public Static Final String constant.
The title of this little summary is Don’t use the wrong companion. What is its premise? The premise is: should you use it? I can’t help but ask: Is there really a need for a partner in this situation? The answer is: no.
The TAG in MainActivity does not need to be accessed outside of the class, so it can be defined directly as a member variable:
class MainActivity : AppCompatActivity() {
private val TAG = "Main"
}
Copy the code
Now that we’re left with EXTRA_PHOTO, what should we do with it? In Java, we often define a class to store constants, and we can use Kotlin to do the same:
Let’s create a constant.kt:
// Notice here that it comes before the package
@file:JvmName("Constant")
package com.boycoder.kotlinjetpackinaction
const val EXTRA_PHOTO = "photo"
const val CACHE_RESPONSE = "..."
Copy the code
In Kotlin you can use it directly like this:
// Kotlin can even omit Constant because CACHE_RESPONSE is a top-level Constant.
display(CACHE_RESPONSE)
Copy the code
Use this in Java:
// Constant.EXTRA_PHOTO is also well accessible in Java thanks to @file:JvmName("Constant")
String url = intent.getStringExtra(Constant.EXTRA_PHOTO);
Copy the code
Constant.kt decompilated to Java looks like this:
public final class Constant {
@NotNull
public static final String EXTRA_PHOTO = "photo";
@NotNull
public static final String CACHE_RESPONSE = "...";
}
Copy the code
So, if you just need to define static constants, why bother with Kotlin’s companion objects?
See my Github Commit for details on the above changes.
Conclusion:
- Java programming experience is also useful in Kotlin, but we can’t be trapped by Java programming experience
- Kotlin introduces features and concepts that are not available in Java, and it is best to understand the underlying implementation before using it
- I wrote a blog on the Internet
Best practices
Not necessarily (including this article), think for yourself
4. At the end
This article only uses our Demo to glimpse the threefold realm of Kotlin programming, so that we have an understanding of Kotlin programming as a whole. I’ll probably write a feature article later on the Hitchhiker’s Guide to the Kotlin Compiler, The Kotlin Best Practices Point North, maybe.
The article says that this is nearing the end, so is our Demo project perfected to this extent? Of course not. But I don’t want to write, you are welcome to leave a comment and discuss what can be improved.
See you in the next article.
Kotlin Jetpack Combat