官术网_书友最值得收藏!

Static methods and companion objects

Unlike Java, Kotlin doesn't support static methods for a class. Most readers will know that static methods do not belong to the object instance but rather to the type itself. In Kotlin, it is advisable to define methods at the package level to achieve the functionality of static methods. Let's define a new Kotlin file and name it Static. Within this file, we will place the code for a function that will return the first character of the input string (if the input is empty, an exception will be raised), which is as follows:

    fun showFirstCharacter(input:String):Char{ 
      if(input.isEmpty()) throw IllegalArgumentException() 
      return input.first() 
    } 

Then in your code, you can simply call showFirstCharacter("Kotlin is cool!"). The compiler is here to do some of the work for you. Using javap, we can take a look at the byte code generated.

Just run javap -c StaticKt.class to get the code produced by the compiler:

Compiled from "Static.kt" public final class com.programming.kotlin.chapter03.StaticKt {
  public static final char showFirstCharacter(java.lang.String);
    Code:
      0: aload_0
      1: ldc           #9                  //String input
      3: invokestatic  #15                 //Method  kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang /Object;Ljava/lang/String;)V
      ...
      40: aload_0
      41: checkcast     #17       //class java/lang/CharSequence
      44: invokestatic  #35                 //Method  kotlin/text/StringsKt.first:(Ljava/lang/CharSequence;)C
      47: ireturn
}

As you can see from the printout, the compiler has actually generated a class for us and has marked it as final; it can't be inherited, as you already know. Within this class, the compiler has added the function we defined. Let’s call this method from the program entry point, and again using the utility javap we can look at what the bytecode looks like:

fun main(args: Array<String>) {
  println("First lettter:" + showFirstCharacter("Kotlin is cool"))
}
    
Compiled from "Program.kt"
public final class com.programming.kotlin.chapter03.ProgramKt {
  public static final void main(java.lang.String[]);
    Code:
      0: aload_0
      ...
      18: ldc           #29                 //String Kotlin is  cool
      20: invokestatic  #35                 //Method  com/programming/kotlin/chapter03/StaticKt.showFirstCharacter:(Ljav a/lang/String;)C
}

Most of the bytecode has been left out for the sake of simplicity, but at line 20 you can see there is a call to our method; the call is made through the invokestatic routine.

We can't talk about static methods and not bring singletons into the discussion. A singleton is a design pattern that limits the instantiation of a given class to one instance. Once created, it will live throughout the span of your program. Kotlin borrows the approach found in Scala. Here is how you can define a singleton in Kotlin:

    object Singleton{ 
      private var count = 0 
      fun doSomething():Unit { 
        println("Calling a doSomething (${++count} call/-s in  total)") 
      } 
    } 

From any function, you can now call Singleton.doSomething, and each time you will see the counter increasing. If you look at the bytecode produced, you will find the compiler is doing some of the work for us once again:

public final class com.programming.kotlin.chapter03.Singleton {
  public static final com.programming.kotlin.chapter03.Singleton  INSTANCE;
    
  public final void doSomething();
    Code:
      0: new           #10      // class java/lang/StringBuilder
      43: return
      ...
      static {};
    Code:
       0: new           #2        //class  com/programming/kotlin/chapter03/Singleton
       3: invokespecial #61            //Method "<init>":()V
       6: return
}

I have left out the code produced for our doSomething method since it is not the focus of this topic. The compiler once again has created a class and marked it as final. Furthermore, it has introduced a member called INSTANCE and has marked it as static. The interesting part is at the end of the listing where you see the static{}; entry. This is the class initializer, and it is called only once; JVM will make sure this happens before all of the following:

  • An instance of the class is created
  • A static method of the class is invoked
  • A static field of the class is assigned
  • A non-constant static field is used
  • An assert statement lexically nested within the class is executed for a top-level class

In this case, the code is called before the first call to doSomething because we access the INSTANCE static member (see the following getstatic bytecode routine). If we were to call this method twice, we would get the following bytecode:

public static final void main(java.lang.String[]);
  Code:
    0: aload_0
    1: ldc           #9                  // String args
    3: invokestatic  #15   //Method  kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang /Object;Ljava/lang/String;)V
    6: getstatic     #21           //Field  com/programming/kotlin/chapter03/Singleton.INSTANCE:Lcom/programmi ng/kotlin/chapter03/Singleton;
     9: invokevirtual #25        //Method  com/programming/kotlin/chapter03/Singleton.doSomething:()V
     12: getstatic     #21        //Field  com/programming/kotlin/chapter03/Singleton.INSTANCE:Lcom/programmi ng/kotlin/chapter03/Singleton;
     15: invokevirtual #25        //Method  com/programming/kotlin/chapter03/Singleton.doSomething:()V
     18: return

You can see that on both occasions doSomething is called as a virtual method. The reason is you can create a singleton that inherits from a given class, as in the following example:

    open class SingletonParent(var x:Int){ 
      fun something():Unit{  
        println("X=$x")
       } 
    } 
    object SingletonDerive:SingletonParent(10){} 

There is a way to call a static method as you would in Java. To achieve this, you will have to place your object within a class and mark it as a companion object. This concept of a companion object will be familiar to someone with at least entry-level knowledge of Scala. The following example uses the factory design pattern to construct an instance of Student:

    interface StudentFactory { 
      fun create(name: String): Student 
    } 
    class Student private constructor(val name: String) { 
      companion object : StudentFactory { 
        override fun create(name: String): Student { 
          return Student(name) 
        } 
      } 
    } 

As you can see, the constructor for the Student type has been marked as private. Thus, it can't be invoked from anywhere apart from inside the Student class or the companion object. The companion class has full visibility for all the methods and members of Student.

From the code, you will need to call Student.create("Jack Wallace") to create a new instance of Student. If you look in the build output, you will notice there are two classes generated for Student—one is Student.class and the other is Student$Companion.class. Let's see how the call to Student.create gets translated into bytecode:

public final class com.programming.kotlin.chapter03.ProgramKt {
  public static final void main(java.lang.String[]);
    Code:
      0: aload_0
      1: ldc           #9               //String args
      3: invokestatic  #15              //Method  kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang /Object;Ljava/lang/String;)V
      6: getstatic     #21              // Field  com/programming/kotlin/chapter03/Student.Companion:Lcom/programmin g/kotlin/chapter03/Student$Companion;
      9: ldc           #23            //String Jack Wallace
      11: invokevirtual #29           //Method  com/programming/kotlin/chapter03/Student$Companion.create:(Ljava/l ang/String;)Lcom/programming/kotlin/chapter03/Student;
      14: pop
      15: return
}

At line 6, you will notice there is a call for a getstatic static member. As you can probably imagine, there is a static field added to the Student class of the Student.Companion type:

public final class com.programming.kotlin.chapter03.Student {
  public static final  com.programming.kotlin.chapter03.Student$Companion Companion;
    
  public final java.lang.String getName();
    
   static {};
   Code:
     0: new           #39   //class  com/programming/kotlin/chapter03/Student$Companion
     3: dup
     4: aconst_null
     5: invokespecial #42         //Method  com/programming/kotlin/chapter03/Student$Companion."<init>":(Lkotl in/jvm/internal/DefaultConstructorMarker;)V
     8: putstatic     #44        //Field  Companion:Lcom/programming/kotlin/chapter03/Student$Companion;
     11: return

public 
com.programming.kotlin.chapter03.Student(java.lang.String,kotlin.jvm.internal.DefaultConstructorMarker);
   Code:
     0: aload_0
     1: aload_1
     2: invokespecial #24        //Method  "<init>":(Ljava/lang/String;)V
     5: return

This code snippet proves the assumption is correct. You can see the Companion member being added to our class. And yet again, the class gets class initializer code generated to create an instance of our companion class. Student.create is shorthand for writing code such as Student.Companion.create(). If you were trying to create an instance of Student.Companion (that is, val c = Sudent.Companion), you would get a compilation error. A companion object follows all inheritance rules.

主站蜘蛛池模板: 太保市| 无棣县| 许昌县| 通渭县| 五家渠市| 北票市| 板桥市| 旬阳县| 景泰县| 台中县| 平江县| 霞浦县| 武强县| 习水县| 金溪县| 灯塔市| 南和县| 永福县| 五常市| 瑞丽市| 青河县| 阜康市| 金坛市| 天水市| 冀州市| 阳谷县| 巴塘县| 乌拉特前旗| 洛浦县| 镇赉县| 龙海市| 江陵县| 林芝县| 皮山县| 江川县| 大埔区| 庆云县| 博白县| 琼海市| 祁阳县| 陆河县|