1. The interface

(1)interfaceThe keyword

Declare a Kotlin interface using the interface keyword instead of class

// Declare the interface
interface Clickable {
    fun click(a)
}

// Implement the interface
class Button: Clickable {
    override fun click(a) = println("I was clicked")}Copy the code

Kotlin uses a colon after the class name to replace the extends and implements keywords in Java. As with Java, a class can implement any number of interfaces, but can only inherit from one class.

The difference between:

  • Similar to the @Override annotation in Java, the Override modifier is used to annotate the methods and properties of the overridden parent class or interface.

  • The use of the Override modifier is mandatory in Kotlin, which avoids accidental overrides caused by writing the implementation method first and then adding the abstract method.

(2) Kotlin method is implemented by default

Interface methods can have a default implementation. Unlike Java 8, which requires you to annotate the default keyword on such implementations, Kotlin has no special annotations for such methods: just provide a method body.

interface Clickable {
    fun click(a)
    // Method with default implementation
    fun showOff(a) = println("I'm clickable!")}interface Focusable {
    fun setFocus(b: Boolean) =
        println("I ${if(b) "got" else "lost"} focus.")
    
    fun showOff(a) = println("I'm focusable!")}Copy the code

To implement this interface, provide an implementation for Click. You can redefine the behavior of the showOff method, or you can simply use the default behavior and ignore it.

If the same class inherits two interfaces, and both interfaces contain methods with default implementation, the class cannot use the methods with default implementation of the two interfaces. Otherwise, an error will occur.

Subclasses call the parent overriding method in the following format: super

.function()

Example:

class Button: Clickable.Focusable {
    override fun click(a) = println("I was clicked")
    override fun showOff(a) {
        super<Clickable>.showOff()
        super<Focusable>.showOff()
    }
}
Copy the code

Implement an interface containing a method body in Java

Kotlin 1.0 was designed with Java 6 in mind and does not support default methods in the interface. So it compiles each interface with a default method into a combination of an ordinary interface and a class that uses the method body as a static function.

2. Open, final, and abstract modifiers: Final by default

(1) Open keyword

Java classes and methods are open by default and can be overridden

Kotlin’s classes and methods are final by default, that is, they cannot be overridden

// This class is open: other classes can inherit it
open class RichButton: Clickable {
    // This function is final: it cannot be overridden in subclasses
    fun disable(a) {}
    
    // This function is open: you can override it in a subclass
    open fun animate(a) {}
    
    // This function overrides an open function and is itself open
    override fun click(a) {}
    
    // Override fun click() {} */
}
Copy the code

Note: Overrides a member of a base class or interface. The overridden member defaults to open.

To change this behavior and prevent subclasses of the class from overwriting, you can explicitly mark overridden members as final

(2) Abstract keyword

When a class is declared abstract, the class cannot be materialized. An abstract class usually contains some abstract members that are not implemented and must be overridden in subclasses.

Abstract members are always open, so there is no need to explicitly use the open modifier.

// This class is abstract: you cannot create instances of it
abstact class Aniamted {
    // This function is abstract: it has no implementation that must be overridden by subclasses
    abstract fun animate(a)
    
    // Non-abstract method functions in abstract classes are not open by default,
    // But can be marked as open
    open fun stopAnimating(a) {}
    fun animateTwice(a)
}
Copy the code

conclusion

You cannot use final, open, or abstract in an interface. Members in an interface are always open and cannot be declared final.

The meaning of access modifiers in the class in Figure 1

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 rewritten Can only be used in abstract classes; An abstract member cannot have an implementation
override Overrides a member of a parent class or interface If final surfaces are not used, overridden members are left open by default

2. Visibility modifiers

Visibility modifiers help control access to declarations in the code base. By limiting the visibility of implementation details in a class, you ensure that when you modify them you don’t risk breaking the code that depends on that class.

The default visibility in Java, package private (default), is not used in Kotlin. Kotlin uses packages only as a way to organize code in a namespace, not as a visibility control.

Internal, which means “visible only within modules”

A module is a group of files compiled together to understand Kotlin. This could be an Intelij IDEA module, an Eclipse project, and so on.

Advantages of internal visibility:

  • Provides a true encapsulation of module implementation details. With Java, this encapsulation is easily broken because external code can define classes into the same package as your code and gain access to your package’s private declarations.
  • Kotlin allows private visibility in top-level declarations, including classes, functions, and attributes. These declarations are only visible in the file in which they are declared.

Figure 2 Kotlin’s visibility modifier

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

Example:

internal open class TalkativeButton: Focusable {
    private fun yell(a) = println("Hey!")

    protected fun whisper(a) = println("Let's talk!")}internal fun TalkativeButton.giveSpeech(a) =
        println("Speech!")
Copy the code

A general rule is that the underlying type of a class and all classes used in the type argument list, or the signature of a function, have the same visibility as the class or function itself.

This rule ensures that you always have access to all types when you need to call a function or inherit a class.

Kotlin’s visibility modifier and Java

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 the private class: in this case it is compiled to a package private declaration (you cannot declare a class private in Java).

Internal: There is no direct equivalent in Java. Package private visibility is a different thing: a module often consists of multiple packages, and different modules may contain declarations from the same package. So the internal modifier becomes public in bytecode.

3. Inner and nested classes: The default is nested classes

In Kotlin you can declare a class in another class. This is useful when encapsulating a helper class or putting some code close to where it will be used.

The difference is that Kotlin’s nested classes cannot access instances of external classes.

Nested classes without explicit modifiers in Kotlin are the same as static nested classes in Java. To turn it into an inner class that holds a reference to an outer class, use the inner modifier

Figure 3. Mapping of nested and inner classes in Java versus Kotlin

Class A is declared 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

The syntax for referencing external class instances in Kotlin is also different from Java. You need to access the Outer class from the Inner class using this@Outer.

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

3. Sealed classes: Define restricted class inheritance structures

Sealed: Adds a sealed modifier to the parent class, which imposes strict restrictions on the subclasses that may be created. All direct subclasses must be nested in the parent class

  • Sealing class Example
// Mark the base class as sealed
sealed class Expr {
    // List all possible classes as nested classes
    class Num(val value: Int): Expr()
    class Sum(val left: Expr, val right: Expr): Expr()
}

fun eval(e: Expr): Int = 
    when(e) {
        is Expr.Num -> e.value
        is Expr.Sum -> eval(e.right) + eval(e.left)
    }
Copy the code
  • Examples of non-sealed classes
class Expr {
    class Num(val value: Int): Expr()
    class Sum(val left: Expr, val right: Expr): Expr()
}

fun eval(e: Expr): Int = 
    when(e) {
        is Expr.Num -> e.value
        is Expr.Sum -> eval(e.right) + eval(e.left)
        else -> 
            throw IllegalArgumentException("Unknown expression")}Copy the code

If you handle all subclasses of Sealed in the WHEN expression, you no longer need to provide the default branch.

The class implied by the sealed modifier is an open class, and you no longer need to explicitly add the open modifier.

When using sealed and adding a new subclass, a when expression that returns a value will fail compilation.

Such as

sealed class Expr {
    // List all possible classes as nested classes
    class Num(val value: Int): Expr()
    class Sum(val left: Expr, val right: Expr): Expr()
    
    // Additional subclasses are added
    class divide(val value: Int): Expr()
}

fun eval(e: Expr): Int =
        when(e) {
            is Expr.Num -> e.value
            is Expr.Sum -> eval(e.right) + eval(e.left)
            // No corresponding judgment is added here
        }
Copy the code

An error is reported:

That is, the WHEN expression must cover everything, adding either a divide or else branch

In this case, the Expr class has a private constructor that can only be called inside the class. You also cannot declare a sealed port. Because of this, the Kot compiler cannot guarantee that no one can implement the interface in Java code.