The public account history article does not support to modify, nuggets classification function is too slow, I will be in the small column long-term maintenance to re-learn Kotlin series of articles.

In this paper, a permanent update address: xiaozhuanlan.com/topic/43198…

preface

Here is the column relearning Kotlin, inspired by Kotlin Vocabulary from the Android Developers team on Medium.

As a long-time Kotlin fan, I have probably expressed my feelings towards Kotlin more than once on my blog.

It’s 2020, and as an Android developer, Kotlin doesn’t make any sense anymore!

Many articles have been written about Kotlin syntax, so what will I write about in this series?

No matter how powerful Kotlin is, it cannot escape running on the JVM. After kotlinc compiles, the resulting.class file is still the same.

So, the best way to learn Kotlin is to actually look at the bytecode. Android Studio provides plug-ins directly, which can be viewed as follows:

Tools -> Kotlin -> Show Kotlin Bytecode

Of course, bytecode is poorly readable, and the IDE provides Decompile, which converts bytecode into Java code.


Thus, we can easily grasp the essence of Kotlin’s various grammars.

Each article in this series will choose a key word or knowledge point to break down the essence of Kotlin and help you quickly and deeply understand Kotlin.

Now let’s enter today’s protagonist object.

directory

  1. What are the uses of object?

  2. Object declaration – a keyword implementation singleton?

  3. Associated object — a substitute for static?

  4. Object expression — Kotlin’s anonymous inner class?

  5. What kind of usage is that?

The body of the

Three uses of object

Kotlin’s object keyword can be used in three ways:

  • Object declaration, typically used to implement singletons
  • Companion objects, like the Java static keyword, can also be used for factory method patterns
  • Object expressions, commonly used in place of Java’s anonymous inner classes

Here’s a look at the essence of each of the three usages.

Object statement

The semantics of object look like this: Define a class and create an instance of it. Both object declarations and the two other uses described below follow this semantics.

As an object declaration, it can be used directly to implement the singleton pattern:

object Singleton{
    fun xxx(a){}
}
Copy the code

Without further ado, Decompile directly looks at Java code:

public final class Singleton {
   public static final Singleton INSTANCE;

   public final void xxx(a) {
   }
  private Singleton(a) {  }   static {  Singleton var0 = new Singleton();  INSTANCE = var0;  } } Copy the code

As you can see from the Java code, this is clearly a singleton pattern.

  • Private constructor
  • Provide instances externally through static fields
  • Directly initialized in a static code block, thread-safe.

When is the static code block executed?

First of all, the class loading stage can be divided into loading, verification, preparation, parsing, initialization, use, uninstall seven steps. The static code block is executed during initialization. So, what scenarios trigger class initialization? There are several scenarios:

  • throughnewInstantiate an object
  • Read and write a static field of a class
  • Call a static method of a class
  • Make a reflection call to the class

Singleton.INSTANCE (singlet. INSTANCE, singlet. INSTANCE, singlet. INSTANCE, singlet. INSTANCE) This completes the instantiation of the singleton. Also, because class loading is inherently thread-safe, Kotlin’s object singleton is a thread-safe lazy singleton (initialized at access).

In addition, the singleton class declared by Object is the same as the ordinary class, which can implement interfaces, inherit classes, and also contain attributes and methods. But it cannot be declared manually by the developer, and as you can see from the decompilated Java code, it has only one private constructor.

So, there are limitations to the actual business scenario. For singleton classes that need to carry parameters, Object is a bit of a drag. Of course, it is not difficult to solve, just imitate the Java writing method, here to DCL mode as an example.

class Singleton private constructor(private val param: Int) {
    companion object {
        @Volatile
        private var instance: Singleton? = null
        fun getInstance(property: Int) =
instance ? : synchronized(this) { instance ? : Singleton(property).also { instance = it } }  } } Copy the code

At this point, you should understand the nature of object’s implementation of the singleton pattern. Now let’s look at the companion objects.

Associated object

Can you recall that you used the static keyword in Kotlin? The answer is definitely no. It is usually possible to define constants and top-level functions directly in top-level files, but sometimes it is more intuitive to define static constants or functions in a class. This is how companion objects are used.

A companion object, as the name implies, is an object that accompanies a class and is initialized when the class is loaded.

class User(val male: Int) {    companion object {
        val MALE = 0
        
        fun isMale(male:Int) = male == MALE
 } } Copy the code

This allows you to call properties and functions in the accompanying object just as you would call static, without creating class instances.

User.MALE
User.isMale(1)
Copy the code

Let’s go straight to the Java code.

public final class User {
   private final int male;
   private static final int MALE = 0;
   public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null);

 public final int getMale(a) {  return this.male;  }   public User(int male) {  this.male = male;  }   public static final class Companion {  public final int getMALE(a) {  return User.MALE;public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null);  }   public final boolean isMale(int male) {  return male == ((User.Companion)this).getMALE();  }   private Companion(a) {  }   // $FF: synthetic method  public Companion(DefaultConstructorMarker $constructor_marker) {  this(a); }  } }  Copy the code

The compiler generates a static inner class called Companion. Note that its getMale() and isMale() methods are not static, so you’ll need a Companion instance to actually access it. This is the static member variable Companion defined in the User class:

public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null);
Copy the code

When you look at static fields, you should think of them as initialized at class load time. So, which operation triggers class loading? Let’s decompilate the Java code for user.male.

User.Companion.getMALE();
Copy the code

So it also initializes the associated object when it is accessed. And if you think back to what I said earlier,

An object can be interpreted as defining a class and creating an instance of it.

The associated objects still conform to this semantics.

How do you call a companion object in Java? User.Com panion. IsMale (1). Alternatively, we can name the associated object as follows:

companion object X { ... }
Copy the code

Instead of Companion, the compiler generates X classes. You can use user.xisale (1) in Java.

Now that we know the nature of the companion object, let’s talk about two other uses of it.

Create a static factory method

interface Car {
    val brand: String

    companion object {
        operator fun invoke(type: CarType): Car {
 return when (type) {  CarType.AUDI -> Audi()  CarType.BMW -> BMW()  }  }  } } Copy the code

The invoke() method is overloaded and can be called directly with Car(Cartype.BMW). You can try implementing the above logic in Java code for comparison.

Associated object extension methods

Companion objects also support extension methods. Using the Car example above, define an extended method to get a Car type by Car brand.

fun Car.Companion.getCarType(brand:String) :CarType { ...... }
Copy the code

Although an extension function defined on Car.Com Panion, it actually adds a static method to Car as follows:

Car.getCarType("BMW")
Copy the code

Object expression

The most classic use of object expressions is in place of Java’s anonymous inner classes. For example, common click events:

xxx.setOnClickListener(object : View.OnClickListener{
        override fun onClick(v: View) {
            
        }
})
Copy the code

This is equivalent to Java’s anonymous inner classes. However, like the single-method interface above, we rarely use object instead of lambda, which is much more concise.

xxx.setOnClickListener { view ->  ...... }
Copy the code

When anonymous objects need to override multiple methods, object expressions are the only choice.

Like Java’s anonymous inner classes, external variables can be accessed in object declarations.

Object expressions are probably the most unpretentious use of object.

The last

By now, you should have mastered the essence of the object keyword. So, I’m going to test you, too. Take a closer look at the following code:

class MainActivity : AppCompatActivity() {
    val a = 1
    
    object click : View.OnClickListener {
        override fun onClick(v: View) {
 val b = a + 1  }  } } Copy the code
  • Does the above code compile correctly? Why is that?
  • hereobjectWhat kind of usage does’?

Leave your answers in the comments section!

This article is formatted using MDNICE

I am bingxin said, pay attention to me, do not get lost!