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

Interfaces

An interface is nothing more than a contract; it contains definitions for a set of related functionalities. The implementer of the interface has to adhere to the interface of the contract and implement the required methods. Just like Java 8, a Kotlin interface contains the declarations of abstract methods as well as method implementations. Unlike abstract classes, an interface cannot contain state; however, it can contain properties. Any Scala developer reading this book will find this similar to traits in Scala:

    interface Document { 
      val version: Long   
      val size: Long 
 
      val name: String     
      get() = "NoName" 
 
      fun save(input: InputStream) 
      fun load(stream: OutputStream) 
      fun getDescription(): String { 
         return "Document $name has $size byte(-s)"} 
    } 

This interface defines three properties and three methods; the name property and getDescription methods provide the default implementation. How would you use the interface from a Java class? Let's see by implementing the following interface:

        public class MyDocument implements Document {

          public long getVersion() {
            return 0;
          }

          public long getSize() {
            return 0;
          }

          public void save(@NotNull InputStream input) {
          }

          public void load(@NotNull OutputStream stream) {
          }

          public String getName() {
            return null;
          }

          public String getDescription() {
            return null;
          }
        } 

You can see the properties have been translated into getters. Despite providing default implementations for getDescription along with the name, you still have to implement them. This is not the case when implementing an interface in a Kotlin class:

    class DocumentImpl : Document {
      override val size: Long
      get() = 0
override fun load(stream: OutputStream) { }
override fun save(input: InputStream) { }
override val version: Long get() = 0 }

Let's delve into the generated code and see what actually happens behind the scenes with the code for those two methods implemented at the interface level:

$ javap -c build\classes\main\com\programming\kotlin\chapter03\DocumentImpl.class
    
public final class kotlin.chapter03.KDocumentImpl implements kotlin.chapter03.Document {
  public long getSize();
      0: lconst_0
      1: lreturn
    
public void load(java.io.OutputStream);
    0: aload_1
    1: ldc           #15                 //String stream
    3: invokestatic  #21                 //Method  kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull
    6: return
    
public void save(java.io.InputStream);
    0: aload_1
    1: ldc           #26                 //String input
    3: invokestatic  #21          //Method  kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull
    6: return
    
public long getVersion();
     0: lconst_0
     1: lreturn
    
public kotlin.chapter03.KDocumentImpl();
     0: aload_0
     1: invokespecial #32      //Method  java/lang/Object."<init>":()V
     4: return
    
public java.lang.String getName();
     0: aload_0
     1: invokestatic  #39                 //Method  kotlin/chapter03/Document$DefaultImpls.getName
     4: areturn
    
public java.lang.String getDescription();
     0: aload_0
     1: invokestatic  #43                 //Method  kotlin/chapter03/Document$DefaultImpls.getDescription
     4: areturn
}

You may have already spotted the calls to the DefaultImpls class in the getDescription and getName code. If you look into the classes produced by the compiler (build/main/com/ programming/kotlin/chapter03), you will notice a file named Document$DocumentImpls.class. What is this class all about, I hear you ask? You haven't written such a class. We can find out what it contains by again turning to javap:

public final class com.programming.kotlin.chapter03.Document$DefaultImpls {
  public static java.lang.String getName(com.programming.kotlin.chapter03.Document);
    Code:
      0: ldc           #9                  //String NoName
      2: areturn
    
public static java.lang.String  getDescription(com.programming.kotlin.chapter03.Document);
     Code:
       0: new           #14        //class java/lang/StringBuilder
       3: dup
       4: invokespecial #18                //Method  java/lang/StringBuilder."<init>":()V
       7: ldc           #20            //String Document
       9: invokevirtual #24               //Method  java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       12: aload_0
       13: invokeinterface #29,  1        //InterfaceMethod  com/programming/kotlin/chapter03/Document.getName:()Ljava/lang/String ;
    
       40: invokevirtual #43            //Method  java/lang/StringBuilder.toString:()Ljava/lang/String;
       43: areturn
}

From the preceding code snippet (I left out some of the code for simplicity), you can clearly see the compiler has created a class for us containing two static methods that match the ones we have implemented in the interface.

While the code for getName is very simple, after all, we just return a string value; the one for getDescription is a bit more complex. The code makes use of StringBuilder to create the string for description purposes. The interesting part is how it goes back to getSize and getName. If you look at line 12aload_0 pushes the Document parameter (the getDescription method takes one parameter) to the stack. The next line makes the call by using invokeinterface to call a method defined by a Java interface. Discussing the details of Java bytecode goes beyond the scope of this book. You can find quite a few details, if you want to know more, with a quick search on the web.

主站蜘蛛池模板: 墨玉县| 三都| 资源县| 桃园县| 琼结县| 巴塘县| 泾阳县| 洱源县| 苍梧县| 宝清县| 日土县| 沅江市| 郁南县| 云和县| 崇文区| 阿拉善盟| 东乌珠穆沁旗| 兰西县| 六盘水市| 聂荣县| 上虞市| 甘泉县| 七台河市| 新疆| 平陆县| 屏山县| 葵青区| 长丰县| 兰溪市| 穆棱市| 惠安县| 盐城市| 资溪县| 南充市| 靖安县| 鄱阳县| 天祝| 香格里拉县| 拜泉县| 阿克苏市| 屏山县|