Write at the beginning: I plan to write a Kotlin series of tutorials, one is to make my own memory and understanding more profound, two is to share with the students who also want to learn Kotlin. The knowledge points in this series of articles will be written in order from the book “Kotlin In Action”. While displaying the knowledge points in the book, I will also add corresponding Java code for comparative learning and better understanding.

Kotlin Tutorial (1) Basic Kotlin Tutorial (2) Functions Kotlin Tutorial (3) Classes, Objects and Interfaces Kotlin Tutorial (4) Nullability Kotlin Tutorial (5) Types Kotlin Tutorial (6) Lambda programming Kotlin Tutorial (7) Operator overloading and Other conventions Higher-order functions Kotlin tutorial (9) Generics


Define the class inheritance structure

Interfaces in Kotlin

Kotlin’s interfaces are similar to those in Java 8: They can contain definitions of abstract methods (methods = functions) and implementations of non-abstract methods (similar to the default methods in Java 8), but they cannot contain any state. Use the interface keyword to define an interface:

interface Clickable {
    fun click()
}
Copy the code

We declare an interface with an abstract method named click. All non-abstract classes that implement this interface need to provide an implementation of this method. Let’s implement the following interface:

class Button : Clickable {
    override fun click() = println("i was clicked")}Copy the code

Kotlin replaces the extends and implements keywords in Java with a colon after class names. As with Java, a class can implement any number of interfaces, but can only inherit from one class. Similar to the @Override annotation in Java, Kotlin uses the Override modifier to mark methods and attributes of overridden parent classes or interfaces. Using the Override modifier is mandatory and will not compile without the Override annotation. This avoids accidental overrides caused by writing out the implementation method before adding the abstract method. Interface methods can have a default implementation. Java 8 requires you to tag such implementations with the default keyword. Kotlin doesn’t need a special identifier, just a method body:

interface Clickable {
    fun click()
    fun showOff() = println("i'm Clickable!"Class Button: Clickable {override fun click() = println(Override fun click() = println("i was clicked")}Copy the code

When implementing this interface in Kotlin, methods that have a default implementation need not be implemented. Note, however, that if you implement the Kotlin interface in Java code, all methods are implemented. There is no default implementation.

class Abc implements Clickable {

    @Override
    public void click() {
    }

    @Override
    public void showOff() {// must implement}}Copy the code

This has to do with the way Kotlin’s default methods are implemented. Let’s look at the implementation first to see why all methods are implemented in Java. We convert the above interface and implementation classes into Java code:

public interface Clickable {
   void click();

   void showOff();

   public static final class DefaultImpls {
      public static void showOff(Clickable $this) {
         String var1 = "i'm Clickable!";
         System.out.println(var1);
      }
   }
}

public final class Button implements Clickable {
   public void click() {
      String var1 = "i was clicked";
      System.out.println(var1);
   }

   public void showOff() { Clickable.DefaultImpls.showOff(this); }}Copy the code

You can see how Kotlin implements the default method of the interface: DefaultImpls, defines a static inner class in this class implements the default method, and parameters is Clickable object, and then give each implementation class (Button) with the implementation and the call by default Clickable. DefaultImpls. ShowOff (this); . Kotlin needs to be compatible with Java 6, so it doesn’t use Java 8’s interface features. Do you find this implementation very similar to the extension functions in the previous chapter?

There is a special case: if your class implements two interfaces, and each interface has a default implementation method of the same name, then the class will adopt the default implementation of that interface. The answer is: none. Instead, you will get a compilation error if you do not show implementation of the interface with the same name.

interface Clickable {
    fun click()
    fun showOff() = println("i'm Clickable!")
}

interface Focusable {
    fun showOff() = println("i'm Focusable!")
}

class Button : Clickable, Focusable {
    override fun showOff() {
        super<Clickable>.showOff()
        super<Focusable>.showOff()
    }

    override fun click() = println("i was clicked")}Copy the code

Here we implement showOff of the same name and invoke the implementation of the parent type. We used the same keyword super as Java. But the syntax is slightly different. In Java, you can put the name of the base class before the super keyword, like Clickable.super.showoff (). In Kotlin, you need to put the name of the base class in Angle brackets: super

.showoff ().

Open, final, and Abstract modifiers: Final by default

The default classes in Java can be inherited and overwritten unless the final keyword is explicitly used, which is usually convenient but poses some problems. Making changes to a base class can lead to incorrect behavior, which is known as a fragile base class problem. Effective Java also advises: Either design and document inheritance, or forbid it. So Kotlin takes the idea that the default is final. If you want to allow subclasses of a class to be created, use the open modifier to identify the class and add the open modifier to every property or method that can be overridden.

Open class RichButton: Clickable {// Open modifier means you can subclass fundisable() {} // This function is final and cannot be overridden by subclasses of Open funanimate() {} // The function is open and can be overridden by subclasses override funclick() {// This function overwrites an open function, so it is also open}Copy the code

If you override a member of a base class or interface, the overridden member also defaults to open. If you want to change this behavior and prevent subclasses from continuing to overwrite, you can explicitly mark the overridden member as final:

open class RichButton : Clickable { 
    final override fun click() {} // display final to prevent subclass overwriting}Copy the code

There is also an Abstract class in Kotlin, which is basically the same as Java except that the default is final:

Abstract class Animated {// Can not create an instance of abstract animate()// Abstract methods must be overwritten by subclasses open funstopAnimating() {}// Display modifiers open funanimateTwice() {}// The normal method defaults to final}Copy the code

Although the interface can be implemented by default, we still use it according to the Java custom. We do not define the default implementation in the interface, but define the default implementation as an Abstract class.

The meaning of the template modifier in the class

The modifier Members of the relevant commentary
final Can’t be rewritten Class members used by default
open Can be rewritten It needs to be clearly stated
abstract Must be heavy Can only be used in abstract classes. Abstract members cannot have implementations
override Overrides a member of a parent class or interface If final is not used, the overridden member is open by default

Visibility modifier: Defaults to public

In general, visibility modifiers in Kotlin are similar to those in Java. You can also use public, protected, and private modifiers. But the default visibility is different, and if the modifier is omitted, the declaration is public. Default visibility in Java — package private. It’s not used in Kotlin. Kotlin uses packages only as a way to organize code in namespaces, not as visibility control. As an alternative, Kotlin provides a new modifier: internal, which means only visible inside the module. A module is a set of Kotlin files compiled together, which could be an Intellij IDEA module, an Eclipse project, a Maven or Gradle project, or a set of files compiled using calls to Ant tasks. The advantage of internal visibility is that it provides a true encapsulation of module implementation details. With Java, this encapsulation is easily broken because external code can define classes in the same package as your code and gain access to your package’s private declarations. There are unique top-level declarations in Kotlin. If you use private visibility in top-level declarations, including classes, functions, and attributes, these declarations will be visible in the file that declares them.

Kotlin’s visibility modifier

The modifier Members of the class The top statement
Public (the default) Visible everywhere Visible everywhere
internal Visible in module Visible in module
protected Visible in subclasses
private Seen in class Visible in the file

Notice how the protected modifier behaves differently in Java and Kotlin. In Java, you can access a protected member from the same package, but in Kotlin the protected member is only visible to the class and its subclasses, meaning that the same package is not visible. Also note that extension functions of the class do not have access to protected members of the class.

The public, protected, and private modifiers in Kotlin are retained when codified into Java bytecode. You use these Kotlin declarations from Java code as if they had declared the same visibility in Java. The only exception is that private classes are compiled into package private declarations (you can’t declare classes private in Java). But what happens to the internal modifier, you might ask? There is no direct equivalent in Java. Package private visibility is an entirely different matter, a module will often consist of multiple packages, and different modules may contain declarations from the same package. So the internal modifier becomes public in bytecode. The correspondence between these Kotlin declarations and their Java equivalents (or their bytecode rendering) explains why you can sometimes access an internal class or top-level declaration from Java code, or access a protected member from Java code in the same package (similar to what you would do in Java). But you should try to avoid this situation to break the visibility constraints.

In addition, there is another difference in visibility rules between Kotlin and Java: an external class in Kotlin cannot see the private members of its internal (or nested) classes.

Inner and nested classes: The default is nested classes

If you’re not sure about Java inner and nested classes, or you can’t remember the details, check out this blog post: Understanding Java nested and inner classes, anonymous Classes

Class Outer {class Inner {// Inner {Nested}}Copy the code

In Java, inner classes hold external class references, which are often easy to ignore and cause memory leaks and unexpected problems. So the default in Kotlin is nested classes, and if you want to declare them as inner classes, you need to use the inner modifier.

The mapping of nested and inner classes in Java versus Kotlin

A declaration of class A in another class B In Java In the Kotlin
Nested classes (do not store references to external classes) static class A class A
Inner class (stores references to external classes) class A inner class A

In Java, the inner class gets the object of the Outer class through Outer. This, whereas in Kotlin it gets the object of the Outer class through this@Outer.

class Outer {
    inner class Inner {
        fun getOuter(): Outer = this@Outer
    }
}
Copy the code

Sealed class: Defines a restricted class inheritance structure

Kotlin provides a sealed modifier for the class to restrict that subclasses must be nested within their parent class.

sealed class Father {
    class ChildA : Father()

    class ChildB : Father()
}
Copy the code

Sealed modifiers imply that this class is an open class, so you no longer need to display the open modifier.

What good is that? When you handle all subclasses of sealed in the WHEN expression, you no longer need to provide the default branch:

fun a(c: Father): Int =
            when (c) {
                is ChildA -> 1
                is ChildB -> 2
//                else-> 3 // covers all possible cases, so it is no longer needed}Copy the code

Classes that declare sealed modifiers can only call the private constructor internally and cannot declare an interface to sealed. Why is that? Remember the rules of visibility when converted to Java bytecode? Otherwise, the Kotlin compiler cannot guarantee that this interface is implemented in Java code.

In Kotlin 1.0, sealed is quite restrictive. All subclasses must be nested, and subclasses cannot be created as data classes (mentioned later). Kotlin 1.1 lifts these restrictions and allows a subclass of Sealed to be defined anywhere in the same file.

Declare a class with a non-default constructor or attribute

One or more constructors can be declared in Java, and Kotlin is similar, with a twist: a distinction is made between primary constructors (usually the primary and concise methods that initialize a class and are declared outside the class body) and slave constructors (declared inside the class body). It is also possible to add additional initialization logic to the initialization statement block.

Initialization class: main constructor and initialization statement block

We’ve seen how to declare a simple class before:

class User (val nickName: String)
Copy the code

The bracketed block (val nickName: String) is called the primary constructor. There are two main purposes: to specify the constructor parameters and to define the properties initialized with those parameters. To see how it works, look at the transformed Java code:

public final class User {
   @NotNull
   private final String nickName;

   @NotNull
   public final String getNickName() {
      returnthis.nickName; } public User(@NotNull String nickName) { this.nickName = nickName; }}Copy the code

We could also implement this logic in Kotlin (which is not necessary at all, just to learn the keyword example, and write it exactly the same as above) :

class User constructor(_nickName: String) {
    val nickName: String

    init {
        nickName = _nickName
    }
}
Copy the code

Two new keywords appear: constructor to start a main constructor or declaration from a constructor (which can be omitted when defining the main constructor with the class name); The init keyword is used to introduce an initialization statement block, much like a construction code block in Java. This is exactly the same as class User (val nickName: String). Notice that the simple script contains the val keyword, which means that the corresponding property is initialized using the constructor’s argument.

Constructors can also be set to default values like function arguments:

class User @JvmOverloads constructor(val nickName: String, val isSubscribed: Boolean = true)
Copy the code

The default parameter reduces the need to define overloaded constructs, and @jvMoverloads allow Java code to create instances with default parameters.

If your class has a parent, the main constructor also needs to initialize the parent. You can do this by providing the parent constructor parameter in the parent reference of the base class list:

open class User(val nickName: String)

class TwitterUser(nickName: String) : User(nickName)
Copy the code

If no constructor is declared for a class, a default constructor is generated that does nothing. Classes that inherit from the class must also display the constructor of the calling parent class:

open class Button

class RadioButton : Button()
Copy the code

Notice the () after Button()? This is also different from interfaces, which have no constructors, so there is no () after the interface:

interface Clickable

class RadioButton : Button(), Clickable
Copy the code

If you want to ensure that the class is not instantiated by other code, add private:

class Secretive private constrauctor()
Copy the code

A more general meaning can be expressed in Java by using the private constructor to disallow instantiation of the class: the class is a container or singleton of static utility classes. Kotlin has built-in language-level functionality for this purpose. You can use top-level functions as static utilities. To represent singletons, you can use object declarations, which will be seen in a later section.

Constructor: Initialize the parent class in different ways

The default arguments already avoid constructor overloading. But if you must declare multiple construction parameters, that’s fine.

open class View {
    constructor(context: Context)

    constructor(context: Context, attributes: Attributes)
} 
Copy the code

This class does not declare the main constructor, but does declare two slave constructors, which must be elicited using the constructor keyword. If you want to extend this class, you can declare the same constructor and call the corresponding parent constructor using the super keyword:

class Button : View {
    constructor(context: Context) : super(context)

    constructor(context: Context, attributes: Attributes) : super(context, attributes)
}
Copy the code

Just as in Java, you can use the this keyword to call one constructor from another constructor in a class.

class Button : View {
    constructor(context: Context) : super(context)

    constructor(context: Context, attributes: Attributes) : this(context)
}
Copy the code

Note that if a primary constructor is defined, all secondary constructors must call the primary constructor directly or indirectly:

open class View() {
    constructor(context: Context) : this()

    constructor(context: Context, attributes: Attributes) : this(context)
}
Copy the code

Implements properties declared in the interface

In Kotlin, interfaces can contain abstract property declarations:

interface User {
    val nickName: String
}
Copy the code

The property is not a variable (field), but val represents the getter method, and the corresponding Java code:

public interface User {
   @NotNull
   String getNickName();
}
Copy the code

We implement this interface in several ways:

class PrivateUser(override val nickName: String) : User

class SubscribingUser(val email: String) : User {
    override val nickName: String 
        get() = email.substringBefore("@") // Only getter method} class FacebookUser(val accountId: Int) : User {override val nickName = getFacebookName(accountId)}Copy the code

The PrivateUser class uses a concise syntax to declare an attribute in the main constructor that implements an abstract attribute from User, so override needs to be marked. The nickName property is implemented through a custom getter that has no support for storing its value and only a getter that gets a nickName from the email each time it is called. The FacebookUser class associates the nickName attribute with the value at initialization. The getFacebookName method is expensive to get user information by associating it with Facebook, so it is called only once during initialization. In addition to abstract property declarations, interfaces can also contain properties with getters and setters, as long as they do not reference a support field (which requires storing state in the interface, which is not allowed) :

interface User {
    val email: String
    val nickName: String 
          get() = email.substringBefore("@")}Copy the code

Access support fields through getters or setters

There are two types of properties: fields or variables, which Kotlin states generate default getters and setters. The other has no fields, just getters and setters, and because they are represented the same in Kotlin, they are called properties. The corresponding Java code shows the difference more clearly:

class Student {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return name.length() > 0 ? name.substring(0, 1) : ""; }}Copy the code

The name attribute is field supported, while the Surname attribute only has the get method. These two attributes are defined in Kotlin as follows:

class Student {
    var name: String = ""
    val surname: String
        get() = if (name.isNotEmpty()) name.substring(0, 1) else ""
}
Copy the code

The field properties declared in Kotlin generate default getters and setters, or you can change the default generation:

class User(val name: String) {
    var address: String = "unspecified"
        set(value: String) {
            println("""
                Address was changed for $name:"$field"- >"$value".""".trimIndent())
            field = value
        }
}
Copy the code

Getter and setter methods can also be defined below the fields in the same way that custom accessors are defined, using the field identifier in the method to indicate the support field. The differences between the two attributes found in Kotlin are subtle: unspecified: = “Unspecified”, and the field field is used.

Modify the visibility of the accessor

The visibility of accessors is the same as that of properties. But it can be modified if needed by placing visibility modifiers before the get and set keywords:

class LengthCounter {
    var counter: Int = 0
        private set

    private var other: Int = 0
}
Copy the code

What’s the difference between placing private directly in front of a property and placing it in front of a set or get accessor? Take a look at the converted Java code:

public final class LengthCounter {
   private int counter;
   private int other;

   public final int getCounter() {
      return this.counter;
   }

   private final void setCounter(int var1) { this.counter = var1; }}Copy the code

Private directly modifies properties without generating getter and setter methods. Modifying a set generates setter methods for private.

Methods generated by the compiler: data classes and class delegates

Generic object method

Let’s start by looking at how the toString, equals, and hashCode methods common in Java are copied in Kotlin.

toString()

class Client(val name: String, val postalCode: Int) {
    override fun toString(): String = "Client(name=$name, postalCode=$postalCode)"
}
Copy the code

equals()

In Java, the == operator compares values when applied to primitive data types and references when applied to reference types. Therefore, equals is usually always called in Java. In Kotlin == is equal to Equals in Java. If you want to compare references in Kotlin, use the === operator.

class Client(val name: String, val postalCode: Int) { override fun equals(other: Any?) : Boolean {if(other == null || other ! Is Client) {// Check if it is a Clientreturn false
        }
        return name == other.name && postalCode == other.postalCode
    }
}
Copy the code

Any is a simulation of java.lang.Object: the parent of all classes in Kotlin. Nullable type Any? Means that other may be null. In Kotlin, all possible null cases need to be indicated, that is, after the type? , which will be explained in the following sections.

hashCode()

The hashCode method is usually overridden with equals because of the generic hashCode contract: if two objects are equal, they must have the same hash value.

class Client(val name: String, val postalCode: Int) {
    override fun hashCode(): Int = name.hashCode() * 31 + postalCode
}
Copy the code

These three methods are usually overridden in the data container bean and are almost automatically generated by the tool, which the Kotlin compiler can now do for us.

Data classes: The practice of automatically generating common methods

We simply add the data keyword to class to define a class that implements toString, equals, and hashCode:

data class Client(val name: String, val postalCode: Int) 
Copy the code

Although the attribute of a data class is not required to be val, it is highly recommended that only read-only attributes be used to make instances of the data class immutable. To make it easier to use immutable objects’ data classes, the Kotlin compiler generates one more method for them, one that allows you to copy instances of the class and modify the values of some properties while copying. Here’s what the copy method looks like manually:

data class Client(val name: String, val postalCode: Int) {
    fun copy(name:String = this, postalCode:Int = this.postalCode) = Client(name, postalCode)
}
Copy the code

Class delegate: Use the “by” keyword

The decorator pattern is commonly used in Java to add some behavior to other classes. The essence of this pattern is to create a new class that implements the same interface as the original class and stores the instance of the original class as a field. Methods that have the same behavior as the original class do not need to be modified and only need to be forwarded directly to the instance of the original class. One disadvantage of this approach is that it requires quite a bit of template code. For example, let’s implement a decorator for the Collection interface, even if you don’t need to modify any behavior:

class DelegatingCollection<T> : Collection<T> {
    private val innerList = arrayListOf<T>()

    override val size: Int = innerList.size
    override fun contains(element: T): Boolean = innerList.contains(element)
    override fun containsAll(elements: Collection<T>): Boolean = innerList.containsAll(elements)
    override fun isEmpty(): Boolean = innerList.isEmpty()
    override fun iterator(): Iterator<T> = innerList.iterator()
}
Copy the code

Now Kotlin has first-class support for delegation as a language-level feature. Whenever you implement an interface, you can delegate the implementation of the interface to another object using the by keyword. Here’s how to rewrite the previous example with recommendations:

class DelegatingCollection<T>(val innerList: Collection<T> = ArrayList<T>()
) : Collection<T> by innerList
Copy the code

All method implementations in the class are gone, the compiler generates them and the implementation is similar to the DelegatingCollection example. In this case, we just need to rewrite the way we need to change behavior:

class CountingSet<T>(
        val innerSet: MutableCollection<T> = HashSet<T>()
) : MutableCollection<T> by innerSet {
    var objectAdded = 0

    override fun add(element: T): Boolean {
        objectAdded++
        return innerSet.add(element)
    }

    override fun addAll(elements: Collection<T>): Boolean {
        objectAdded++
        return innerSet.addAll(elements)
    }
}
Copy the code

This example counts by overwriting the add and addAll methods and delegating the remaining implementation of the MutableCollection interface to the wrapped container.

The object keyword: combines declaring a class with creating an instance

The object keyword in Kotlin comes up in a variety of ways, but they all follow the same core idea: this keyword defines a class and creates an instance (object) at the same time. Let’s look at different scenarios in which it can be used:

  • Object declarations are a way of defining singletons.
  • A companion object can hold factory methods and other methods that are associated with the class but do not depend on the class instance when invoked. Their members can be accessed by the class name.
  • Object expressions are used to replace Java’s anonymous inner classes.

Object declarations: Creating singletons is a snap

The singleton pattern is one of the most commonly used design patterns in Java. Kotlin provides the highest level of language support for all of this by using object declaration capabilities. Object declarations combine a class declaration with a single instance declaration of that class.

object Payroll {
    val allEmployees = arrayListOf<Person>()

    fun calculateSalary() {
        for (person inallEmployees) { ... }}}Copy the code

Like classes, an object declaration can contain declarations for attributes, methods, initialization blocks, and so on. The only thing that is not allowed is constructors. Unlike an instance of a normal class, object declarations are created immediately upon definition, without needing to invoke constructors elsewhere in the code. Like variables, object declarations allow you to use object names. Characters to call methods and access properties:

Payroll.allEmployees.add(Person(...) ) Payroll.calculateSallary()Copy the code

Want to know how it works? Let’s also look at the converted Java code:

public final class Payroll {
   @NotNull
   private static final ArrayList allEmployees;
   public static final Payroll INSTANCE;

    private Payroll(){
    }

   @NotNull
   public final ArrayList getAllEmployees() {
      return allEmployees;
   }

   public final void calculateSalary() {... } static { Payroll var0 = new Payroll(); INSTANCE = var0; allEmployees = new ArrayList(); }}Copy the code

You can see that the constructor is privatized and the Payroll INSTANCE is initialized via a static code block, stored in the INSTANCE field, which is why it is used this way in Java:

Payroll.INSTANCE.calculateSalary()
Copy the code

This INSTANCE is the INSTANCE that will be created when the Payroll class is loaded into memory. Therefore, it is not recommended to declare classes that are too dependent or expensive as singletons using Object.

You can also create a singleton in a class using an object declaration that accesses the private property in the external class:

Data class Person(val name: String) {// Define object NameComparator: Comparator<Person> {override fun compare(o1: Person, o2: Person): Int = o1.name.compareTo(o2.name) } } val persons = listOf(Person("Bob"), Person("Alice"(persons.namecomparator) // callCopy the code

Companion objects: factory methods and static member sites

Classes in Kotlin cannot have static members: The Java static keyword is not part of the Kotlin language. Instead, Kotlin relies on package-level functions (which replace Java’s static methods in most cases) and object declarations (which replace Java’s static methods in other cases, as well as static fields). In most cases, top-level functions are still recommended, but they do not have access to the private members of the class. In particular, how do you define static members that are used in common Java factory methods and classes? Something like this:

static class B {
        public static final String tag = "tag";
        
        private B() {
        }

        public static B newInstance() {
            returnnew B(); }}Copy the code

This is where the companion object comes in. Companion objects are marked by a special keyword added to the object defined in the class: Companion. By doing so, we gain the ability to access the object’s methods and properties directly from the container class name, without having to specify the object’s name. The resulting syntax looks a lot like static method calls in Java:

class A private constructor() {
    companion object {
        fun newInstance() = A()
        val tag = "tag"
    }
}

A.newInstance()
A.tag
Copy the code

An accompanying object used as a normal object

A companion object is essentially a normal object, and a normal object can do everything that a companion object can do, such as implementing an interface. It looks strange because we just left out the class name, or we can add the class name to it:

class A private constructor() {
    companion object C{
        val tag = "tag"Fun newInstance() = A()}} a.c.newinstance ()Copy the code

If you omit the name of the Companion object, the default name will be Companion. This occurs when the code is converted to Java code:

public final class A {
   @NotNull
   private static final String tag = "tag";
   public static final A.Companion Companion = new A.Companion();

   private A() {
   }

   public static final class Companion {
      @NotNull
      public final String getTag() {
         return A.tag;
      }

      @NotNull
      public final A newInstance() {
         return new A((DefaultConstructorMarker)null);
      }

      private Companion() {}}}Copy the code

So, you should understand that calling Companion objects in Java looks like this: A. Companion. NewInstance (). To make the calls in Java feel consistent, use the @jVMStatic annotation on the corresponding member to do this. If you want to declare a static field, you can use the @jVMField annotation on a top-level attribute or on an attribute declared in Object.

class A private constructor() {
    companion object{
        @JvmField
        val tag = "tag"
        @JvmStatic
        fun newInstance() = A()
    }
}
Copy the code

Since the associated object is a normal class, it is possible to declare extension functions:

fun A.Companion.getFlag() = "flag"

A.getFlag()
Copy the code

Object expressions: Anonymous inner classes written differently

The object keyword can be used to declare not only singleton objects, but also anonymous objects. Let’s look at Java code that uses anonymous inner classes as follows:

public static void main(String[] args) {
        new B().setListener(new Listener() {
            @Override
            public void onClick() {}}); } interface Listener { void onClick(); } static class B { private Listener listener; public voidsetListener(Listener listener) { this.listener = listener; }}Copy the code

Using anonymous inner classes in Kotlin:

fun main(args: Array<String>) {
    B().setListener(object : Listener {
        override fun onClick() {}})}Copy the code

The syntax is the same as the object declaration, except that the name of the object is removed. An object expression declares a class and creates an instance of the class, but does not assign a name to the class or instance. Usually they don’t need a name, because you’ll use the object as an argument to a function call. If you need to assign a name to an object, you can store it in a variable. Unlike Java anonymous inner classes, which can only extend one class or implement one interface, Kotlin’s anonymous objects can implement multiple interfaces. And access to variables in functions that create anonymous inner classes is not limited to final variables, you can also modify the value of variables in object expressions:

fun main(args: Array<String>) {
    var clickCount = 0 
    B().setListener(object : Listener {
        override fun onClick() {clickCount++ // modify variable}})}Copy the code

Again, we examine why these differences can be made by looking at the Java code for the transformation:

public static final void main(@NotNull String[] args) {
      final IntRef clickCount = new IntRef();
      clickCount.element = 0;
      (new B()).setListener((Listener)(new Listener() {
         public void onClick() { int var1 = clickCount.element++; }})); }Copy the code

You can see that the clickCount we defined is wrapped with IntRef, so the final property is declared on the wrapper class. How does Kotlin’s anonymous object implement multiple interfaces? I’ve defined a new interface that lets anonymous inner classes implement both interfaces:

fun main(args: Array<String>) {
    var clickCount = 0
    val niming = object : Listener, OnLongClickListener {
        override fun onLongClick() {
        }

        override fun onClick() {
            clickCount++
        }
    }
    B().setListener(niming)
    View().onLongClickListener = niming
}

interface OnLongClickListener {
    fun onLongClick()
}

class View {
    var onLongClickListener: OnLongClickListener? = null
}
Copy the code
public static final void main(@NotNull String[] args) {
      final IntRef clickCount = new IntRef();
      clickCount.element = 0;
      <undefinedtype> niming = new Listener() {
         public void onLongClick() {
         }

         public void onClick() { int var1 = clickCount.element++; }}; (new B()).setListener((Listener)niming); (new View()).setOnLongClickListener((OnLongClickListener)niming); }Copy the code

A new thing

is literally an undefinedtype and can be cast to the corresponding interface. This may not be Java content, and it is not clear what the implementation is.