Kotlin learning notes - interfaces, abstract classes, generics, extensions, set operators, interoperability with Java, singletons

Posted by prcollin on Sun, 07 Nov 2021 21:49:51 +0100

Note: the coding tool is IntelliJ

catalogue

Interface

Example

Define variables

Example

Variable defaults

abstract class

Example

Implementation interface

generic paradigm

Generic class

Generic Functions

Generic implementation of custom map transformation

Joint generic function implementation of generic classes

Generic function implementation

Type constraint

Generic vararg parameter

vararg example

Generic vararg

Type judgment

out and in keywords

extend

spread function

Extended properties

Extension to generics

Infix: infix expression

Use with generics

set operator

map

flatMap

filter

zip

Interoperability with Java

Receive Java data with nullable types

@file:JvmName()

@JvmField

@JvmOverloads

@JavaStatic

Single case

Hungry Han style

Lazy style

Interface

        The interface is open by default, and the interface members are open by default.

Example

interface InterfaceTest {
    fun test()
}

class InterfaceTestImpl : InterfaceTest{
    override fun test() {
        println("This is implemented from InterfaceTest Method of")
    }
}

fun main() {
    InterfaceTestImpl().test()
}

Output:

This is implemented from InterfaceTest Method of

Define variables

        The implementation class can override the variables of the interface through constructor parameters or within the class.

        There can be function default implementation inside the interface.

Example

interface InterfaceVariable {
    var name : String
    val des: String

    fun show(){
        println("name = $name, des = $des")
    }
}

class InterfaceVariableImpl(override var name: String,
                            override val des: String) : InterfaceVariable

class InterfaceVariableImpl2(name: String, des: String): InterfaceVariable{
    override var name: String = name
    override val des: String = des
}

fun main() {
    InterfaceVariableImpl("Happy", "Word").show()
    InterfaceVariableImpl2("Terminal", "When").show()
}

Output:

name = Happy, des = Word
name = Terminal, des = When

Variable defaults

        Variables defined by the interface cannot be directly assigned with =, but variables modified by val can be assigned with get attribute.

        Interface variables with default values can be overridden or not.

interface InterfaceVariable2 {
    val randNum: String
        get() = (1..100).shuffled().first().toString()
    val des : String
        get() = "Don't ask me where I come from"

    fun show(){
        println("randNum = $randNum, des = $des")
    }
}

class InterfaceVariable2Impl: InterfaceVariable2 // If the variables in the interface have default values, they can not be overloaded

class InterfaceVariable2Impl2(randNum: String, des: String): InterfaceVariable2{
    override val randNum: String = randNum
    override val des: String = des
}

fun main() {
    InterfaceVariable2Impl().show()
    InterfaceVariable2Impl2("hahaha", "song").show()
}

Output:

randNum = 89, des = Don't ask me where I come from
randNum = hahaha, des = song

abstract class

        Abstract classes are open by default, and abstract functions and attributes of abstract classes are open by default.

        Abstract classes can have non Abstract member variables and non abstract functions.

        If you want to override non Abstract member variables and non abstract functions, you need to add the open keyword.

Example

abstract class Abstract(age: Int) {
    abstract var name: String
    open var age: Int = age
    open fun show() {
        println("age = $age")
    }

    abstract fun show2()
}

class SubAbstract(override var name: String, override var age: Int) : Abstract(age) {
    override fun show2() {
        println("name = $name, age = $age")
    }

    override fun show() {
        println("override show")
    }
}

fun main() {
    val subAbstract = SubAbstract("Sjh", 30)
    subAbstract.show()
    subAbstract.show2()
}

Output:

override show
name = Sjh, age = 30

Implementation interface

        Abstract classes can implement interfaces, either overriding interface members or not.

interface Animal{
    fun show()
}

abstract class Cat: Animal

abstract class Dog: Animal{
    override fun show() {
        println("Dog Rewritten show")
    }
}

class Poodle: Dog()

fun main() {
    Poodle().show()
}

Output:

Dog Rewritten show

generic paradigm

Generic class

class GenericClass<T>(val t: T){
    fun printAny() = println(t)
}

fun main() {
    GenericClass(123).printAny()
    GenericClass("hello").printAny()
    GenericClass(123.3987f).printAny()
    GenericClass('C').printAny()
    GenericClass(3214.908).printAny()
}

Output:

123
hello
123.3987
C
3214.908

Generic Functions

fun <TYPE> getSelf(t: TYPE) = t.takeIf { t != null } ?: "t is null"

fun main() {
    println(getSelf(null))
    println(getSelf("Today is Sunday"))
    println(getSelf(234))
}

Output:

t is null
 Today is Sunday
234

Generic implementation of custom map transformation

map transformation transforms a type of input into a type of output

Joint generic function implementation of generic classes

package step_six

class GenericClassMap<I>(val input: I, val isMap : Boolean = true){
    fun <O> map(action:(I)->O) = action(input).takeIf { isMap }
}

fun main() {
    println(GenericClassMap("i love this").map {
        it.uppercase()
    })
}

Output:

I LOVE THIS

Generic function implementation

private fun <I, O> map(input: I, isMap: Boolean = true, action: (I) -> O) 
    = action(input).takeIf { isMap }

fun main() {
    println(map("into the unknown") {
        it.uppercase()
    })
}

Output:

INTO THE UNKNOWN

Type constraint

        Similar to Java's t extensions charsequence, Kotlin is written as T: CharSequence.

open class SuperObject(val name: String)

open class Human(val humanName: String): SuperObject(humanName)

class Man(val manName: String): Human(manName)

class Woman(val womanName: String): Human(womanName)

class Other(val name: String)

fun <T: Human> show(t: T){ // Only Human and subclass objects can be received
    println("name = ${t.humanName}")
}

fun main() {
//    show(SuperObject("super object") / / compilation fails
    show(Human("human"))
    show(Man("man"))
    show(Woman("woman"))
//    show(Other("other") / / compilation fails
}

Output:

name = human
name = man
name = woman

Generic vararg parameter

         vararg is equivalent to a Java variable parameter.

vararg example

fun show(vararg arr: Int){
    arr[0] = 1
    arr.forEach {
        println(it)
    }
}

fun main() {
    show(-1)
}

Output:

1

Generic vararg

        The generic vararg parameter can only be received with the generic parameter array decorated with out. It can only read its elements and cannot be modified.

private fun <T> show(vararg ts: T){
    ts.forEach {
        print("$it ")
    }
}

fun main() {
    show(1, 2, 3)
}

Output:

1 2 3 

Type judgment

        When performing is and as operations on generic parameters, nullable data types should be followed, because generic parameters can receive null.

private fun <T> show(t : T){
    if(t is String?){
        println(t?.length ?: null)
    }else if(t is Int?){
        println(t ?: null)
    }else{
        println("Other types")
    }
}

fun main() {
    show(null)
    show("Hope")
    show(123)
    show('C')
}

Output:

null
4
123
 Other types

out and in keywords

        out:         

                 out T is equivalent to Java? The difference between extensions T and Java is that out T can only be declared on classes or interfaces.

                 When out T is declared on a class or interface, all functions of the class can only take T-type variables as return values, not as function input parameters.  

        in:

                 in T is equivalent to Java? super T is different from Java in that in T can only be declared on classes or interfaces.

                When in T is declared on a class or interface, all functions of the class can only take T-type variables as input parameters, not as function return values.

extend

spread function

        The member function of a class defined outside the class can be called like the member function inside the class to access the members inside the class.

class ExpandTest(val info: String)

fun ExpandTest.show() = println(info)

fun main() {
    ExpandTest("spread function ").show()
}

Output:

spread function 

Extended properties

         The member properties of a class defined outside the class can be called like the member properties inside the class.

class ExpandField(val info: String)

val ExpandField.infoLength
    get() = info.length

fun main() {
    println(ExpandField("Extended properties").infoLength)
}

Output:

4

be careful:

         If the access scope of extension functions and extension attributes is not limited, they can be accessed globally.

        If you want nullable types to also call extended functions or extended properties, you need to extend nullable types.

        Extension functions and extension attributes can be defined in separate files for easy search and maintenance.

Extension to generics

        Implementation principle of built-in functions such as apply, let, run, with, also, takeIf and takeUnless.

private fun <T> T.log() = println(this)

fun main() {
    123.log()
    "abc".log()
    'C'.log()
    null.log()
}

Output:

123
abc
C
null

Infix: infix expression

infix fun String.infixTest(i: Int){
    println("$this:::$i")
}

fun main() {
    "abcd" infixTest 1324
}

Output:

abcd:::1324

Use with generics

        A wider range of applications, such as to, can generate any type of Pair variable.

private infix fun <T, X> T.union(x: X){
    println("t = $this, x = $x")
}

fun main() {
    8734.2 union "abc"
}

Output:

t = 8734.2, x = abc

tips: as can be used to alias the guided package, which can improve development efficiency and solve package conflicts.

set operator

map

        You can add a collection element to another collection after some transformation.

fun main() {
    listOf("Beijing", "Shanghai", "Nanjing").map {
        it.length
    }.forEach{
        print("$it ")
    }
}

Output:

7 8 7 

flatMap

         Some transformations can be made to the collection elements, and the lambda of flatMap must return a collection.

fun main() {
    listOf("Zhang San", "Li Si", "Wang Wu").flatMap {
        listOf("&$it&")
    }.forEach { print("$it ") }
}

Output:

&Zhang San& &Li Si& &Wang Wu& 

filter

        Select the matching elements according to the filter conditions.

fun main() {
    listOf("Hi", "Hello", "What", "World", "Happy")
        .filter {it.contains("H")}
        .forEach { print("$it ") }
}

Output:

Hi Hello Happy 

zip

        Merge the two sets, encapsulate the corresponding subscript elements into a Pair, and finally put them into a new set.

fun main() {
    val words = listOf("Hi", "Hello", "Hope", "Happy", "Work")
    val lengths = listOf(2, 5, 4, 5, 4)
    words.zip(lengths).forEach {
        println("word = ${it.first}, length = ${it.second}")
    }
}

Output:

word = Hi, length = 2
word = Hello, length = 5
word = Hope, length = 4
word = Happy, length = 5
word = Work, length = 4

Interoperability with Java

Receive Java data with nullable types

Java code:

package communicate_with_kotlin;

public class ProvideValue {

    public static String getInfo(){
        return null;
    }

    public static String getString(){
        return "test";
    }
}

Kotlin Code:

import communicate_with_kotlin.ProvideValue

fun main() {
    val info: String? = ProvideValue.getInfo()
    val string: String? = ProvideValue.getString()
    println(info?.length ?: "info Is null")
    println(string?.length ?: "string Is null")
}

Output:

info Is null
2

@file:JvmName()

        Used to define the class name generated by the Kotlin file, which must be written in front of the package name.

Kotlin Code:

@file:JvmName("NewClassName")
package step_six

fun jvmName(){
    println("test JvmName")
}

Java code:

import step_six.NewClassName;

public class JvmNameTest {
    public static void main(String[] args) {
        NewClassName.jvmName();
    }
}

Output:

test JvmName

@JvmField

        You can make Java code directly access Kotlin variables without using the get method.

Kotlin Code:

package step_six

class JvmFieldTest(@JvmField val msg: String)

Java code:

import step_six.JvmFieldTest;

public class JvmField {
    public static void main(String[] args) {
        System.out.println(new JvmFieldTest("JvmField test").msg);
    }
}

Output:

JvmField test

@JvmOverloads

        You can enable Java code to use Kotlin's default parameter feature.

Kotlin Code:

package step_six

@JvmOverloads
fun show(name: String, age: Int = 99, gender: Char = 'F')
{
    println("name = $name, age = $age, gender = $gender")
}

Java code:

package communicate_with_kotlin;

import step_six.JvmOverloadsTestKt;

public class JvmOverloads {
    public static void main(String[] args) {
        JvmOverloadsTestKt.show("ss");
        JvmOverloadsTestKt.show("gg", 14);
        JvmOverloadsTestKt.show("h", 30);
    }
}

Output:

name = ss, age = 99, gender = F
name = gg, age = 14, gender = F
name = h, age = 30, gender = F

@JavaStatic

        Make java code call Kotlin's function like calling Java's static method.

Kotlin Code:

package step_six

class JvmStaticTest {
    companion object{
        @JvmStatic
        fun show(){
            println("JvmStatic test")
        }
    }
}

Java code:

public class JvmStatic {
    public static void main(String[] args) {
        JvmStaticTest.show();
    }
}

Output:

JvmStatic test

Single case

Hungry Han style

object EHanSingleton{
    fun show(){
        println("Kotlin Hungry Han style single case")
    }
}

fun main() {
    EHanSingleton.show()
}

Output:

Kotlin Hungry Han style single case

Lazy style

package step_six

class LanHanSingleton private constructor(){

    init{
        println("The main constructor is executed")
    }

    companion object{
        val INSTANCE: LanHanSingleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED){
            LanHanSingleton()
        }
    }

    fun show(){
        println("Kotlin Lazy single case")
    }
}

fun main() {
    LanHanSingleton.INSTANCE.show()
    LanHanSingleton.INSTANCE.show()
}

Output:

The main constructor is executed
Kotlin Lazy single case
Kotlin Lazy single case

Topics: Java kotlin