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

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.

主站蜘蛛池模板: 斗六市| 沐川县| 汕头市| 阜城县| 始兴县| 荥阳市| 东方市| 嵩明县| 囊谦县| 沾益县| 潼南县| 湖南省| 阳江市| 松阳县| 察隅县| 咸丰县| 竹北市| 海南省| 马关县| 嘉定区| 库尔勒市| 托克逊县| 大悟县| 永寿县| 阿勒泰市| 泰州市| 河曲县| 东源县| 民勤县| 依安县| 封开县| 东平县| 天柱县| 蒲城县| 大化| 滨州市| 孟津县| 青冈县| 姜堰市| 二连浩特市| 水城县|