Translation description:

A few facts about Companion Objects

Original address:Blog.kotlin-academy.com/a-few-facts…] (Blog.kotlin-academy.com/a-few-facts…)

Author:David Blanc

One of the biggest changes Kotlin has made to Java developers is the abolition of static modifiers. Unlike Java, Kotlin’s classes do not allow you to declare static members or methods. Instead, you have to add Companion objects to the class to wrap these static references: The difference may seem small, but it has some significant differences.

First, a Companion object is a singleton instance of an actual object. You can actually declare a singleton in your class and use it as a companion object. This means that in real development, you can’t just use one static object to manage all your static properties! The companion keyword is really just a shortcut that allows you to access the contents of an object by its class name (the companion object’s class name can be omitted if the companion object exists in a particular class and only uses the names of methods or properties in the class). All three lines in the testCompanion () method below are valid statements for compilation purposes.

class TopLevelClass {

    companion object {
        fun doSomeStuff(a){... }}object FakeCompanion {
        fun doOtherStuff(a){... }}}fun testCompanion(a) {
    TopLevelClass.doSomeStuff()
    TopLevelClass.Companion.doSomeStuff()
    TopLevelClass.FakeCompanion.doOtherStuff()
}
Copy the code

For fairness of compatibility, the Companion keyword also provides more options, especially related to Java interoperability. If you try to write the same test code in a Java class, the invocation might be slightly different:

public void testCompanion(a) {
    TopLevelClass.Companion.doSomeStuff();
    TopLevelClass.FakeCompanion.INSTANCE.doOtherStuff();
}
Copy the code

The difference is that Companion is open as a static member in Java code (it’s actually an object instance, but it’s a little misleading because its name starts with an uppercase C), while FakeCompanion references the class name of our second singleton object. In the second method call, we need to use its INSTANCE property to actually access the INSTANCE in Java (you can open the “Show Kotlin Bytecode” menu bar in IntelliJ IDEA or AndroidStudio, And click the “Decompile” button to view the decompiled Java code.

In both cases (whether Kotlin or Java), using Companion Object Companion is much shorter than the call syntax of the FakeCompanion class. In addition, because Kotlin provides annotations, you can let the compiler generate short calls that can still be called in Java code in the same short form as in Kotlin.

The @jVMField annotation, for example, tells the compiler not to generate getters and setters, but to generate Java members instead. Using this annotation to tag a member in the scope of the accompanying object has the side effect of marking that the member is not in the scope of the accompanying object, but exists as a static member of one of Java’s outermost classes. From Kotlin’s perspective, this doesn’t make much difference, but if you look at the decompile bytecode, you’ll notice that the companion object and its members are declared at the same level as the static members of the outermost class.

Another useful annotation is @jvmstatic. This annotation allows you to call methods declared in companion objects just as if they were static methods of the outer class. Note, however: in this case, the method is not moved out of the inner scope of the companion object as the members above are. Because the compiler just adds an extra static method to the outer class, and then delegates it to the companion object inside that method.

Let’s take a look at this simple Kotlin class example:

class MyClass {
    companion object {
        @JvmStatic
        fun aStaticFunction(a){}}}Copy the code

Here is the Java simplified version of the corresponding code:

public class MyClass {
    public static final MyClass.Companion Companion = new MyClass.Companion();
    fun aStaticFunction(a) {// Add an extra static method to the outer class
        Companion.aStaticFunction();// The method is internally delegated to the aStaticFunction method of the companion object
    }
    public static final class Companion {
         public final void aStaticFunction(a) {}}}Copy the code

There is a very subtle difference here, but it can go wrong in some special circumstances. For example, consider the Module in the Dagger. When defining a Dagger module, you can use static methods to improve performance, but if you choose to do so, the compilation will fail if your module contains anything other than static methods. Because Kotlin contains both static methods and static companion objects in its class, it is not possible to write a Kotlin class containing only static methods in this way.

But don’t give up so soon! This doesn’t mean you can’t do it, but it requires a slightly different approach: in this particular case, you can use a Kotlin singleton (using an object expression instead of a class) to replace a Java class with a static method and use the @jVMStatic annotation on each method. In this case, the generated bytecode no longer displays any associated objects, and static methods are attached to the class.

@Module
object MyModule {

    @Provides
    @Singleton
    @JvmStatic
    fun provideSomething(anObject: MyObject): MyInterface {
        return myObject
    }
}
Copy the code

Once again, you see that companion objects are only a special case of singletons. But it at least shows that contrary to what many people believe, you don’t necessarily need a companion object to maintain static methods or static variables. You don’t even need an object to maintain at all, just consider top-level functions or constants: they will be included as static members in an automatically generated class (by default, for example, MyFileKt will be generated as the class name of the myfile.kt file, usually ending in kt).

We’ve gotten a little off track with this article, so let’s go back to companion objects. Now that you know that companion objects are objects, you should also realize that they open up many more possibilities, such as inheritance and polymorphism.

This means that your companion object is not anonymous without a type or superclass. Not only can it have a parent class, but it can even implement interfaces and contain object names. It does not need to be called companion. That’s why you can write a Parcelable class like this:

class ParcelableClass() : Parcelable {

    constructor(parcel: Parcel) : this(a)override fun writeToParcel(parcel: Parcel, flags: Int) {}

    override fun describeContents(a) = 0

    companion object CREATOR : Parcelable.Creator<ParcelableClass> {
        override fun createFromParcel(parcel: Parcel): ParcelableClass = ParcelableClass(parcel)

        override fun newArray(size: Int): Array<ParcelableClass? > = arrayOfNulls(size) } }Copy the code

Here, the accompanying object is named CREATOR, which implements the Parcelable. CREATOR interface in Android, allowing adherence to the Parcelable convention while remaining more intuitive than adding a CREATOR object to the accompanying object using the @jVMField annotation. The @parcelize annotation was introduced in Kotlin so that you can get all the boilerplate code, but that’s beside the point…

To make it even cleaner, if your companion object can implement the interface, it can even use a proxy in Kotlin to do this:

class MyObject {
    companion object : Runnable by MyRunnable()
}
Copy the code

This will allow you to add static methods to multiple objects at once! Note that the companion object does not even need a scope body in this case because it is provided by the proxy.

Last but not least, you can define extension functions for companion objects! This means that you can add static methods or static properties to existing classes, as shown in the following example:

class MyObject {

    companion object

    fun useCompanionExtension(a) {
        someExtension()
    }

}

fun MyObject.Companion.someExtension(a) {}// Define the extension function
Copy the code

What’s the point? I really don’t know. Although Marcin Moskala recommends using this operation to add static factory methods to your classes as Companion extension functions.

In summary, companion objects are not just designed to provide a solution for situations where static modifiers are missing:

  • They are true Kotlin objects, including names and types, as well as some additional functionality.
  • They can even be used in scenarios that are not just to provide static members or methods. There are more options, such as functions that can be used as singletons or as alternatives to top-level functions.

Like most scenarios, Kotlin means a little bit of a shift in your design process, but compared to Java, it doesn’t really limit your options. If there is, it will be by providing you with new, cleaner ways to use it.

Welcome to the Kotlin Developer Alliance, where you can find the latest Kotlin technical articles. We will translate one foreign Kotlin technical article every week. If you like Kotlin, please join us ~~~

Kotlin series articles, welcome to check out:

Kotlin encounters Design Patterns series:

  • When Kotlin Meets Design Patterns perfectly: Singletons (part 1)

Data structure and algorithm series:

  • Binary Search of the Mondays algorithm (described by Kotlin)

Kotlin Original Series:

  • How to fully parse the type system in Kotlin
  • How can you make your callbacks more Kotlin
  • Jetbrains developer daily (part 3) Kotlin1.3 new features
  • New features in Kotlin1.3 (Contract and coroutines)
  • JetBrains Developer Daily News (part 1) : a taste of Kotlin/Native
  • How to Overcome the difficulties of Generics in Kotlin
  • How to Overcome the difficulties of Generics in Kotlin (Part 2)
  • How to Overcome generics in Kotlin (Part 1)
  • Kotlin’s unique secret: Reified Type parameters (Part 2)
  • Everything you need to know about the Kotlin property broker
  • Discussion on source analysis of Sequences in Kotlin
  • Complete parsing of Kotlin’s collections and Functional APIS – Part 1
  • A Brief introduction to Kotlin syntax: Complete parsing of the process of compiling lambda into bytecode
  • Complete parsing of Lambda expressions in Kotlin syntax
  • Extension functions in Kotlin syntax
  • A brief introduction to Kotlin syntax: top-level functions, infix calls, and deconstruction statements
  • How to make functions better to call
  • On variables and constants in Kotlin grammar
  • Basic grammar of Kotlin grammar

Effective Kotlin translation series

  • The Effective Kotlin series considers Using primitive arrays for performance optimization (5)
  • Using Sequence To optimize a set (part 4)
  • Exploring the inline modifier in High-order Functions of the Effective Kotlin series
  • When you encounter multiple constructor parameters, consider using the Constructor (part 2)
  • The Effective Kotlin series considers replacing constructors with static factory methods (part 1)

Translation series:

  • Remember a PR for Kotlin official document translation (inline class)
  • Auto-boxing and High Performance For Inline Classes in Kotlin (part 2)
  • In Kotlin, inline classes are fully parsed.
  • Reified Type parameters (Part 1)
  • When should Type parameter constraints be used in Kotlin generics?
  • A simple way to remember Kotlin parameters and arguments
  • Should Kotlin define functions or attributes?
  • How to remove all of them in your Kotlin code!! (Non-empty assertion)
  • Master Kotlin’s standard library functions: run, with, let, also, and apply
  • Everything you need to know about Kotlin type Aliases
  • Should We use Sequences or Lists in Kotlin?
  • Kotlin’s turtle List rabbit race

Actual combat series:

  • Kotlin ImageSlimming ImageSlimming with Kotlin ImageSlimming
  • Use Kotlin to compress images
  • Use Kotlin wani a picture compression plug-in – actual combat (3)
  • Kotlin actual combat part of the custom View picture rounded corner simple application