• Kotlin基础——接口和类


    接口

    • 使用 : 表示继承关系,只能继承一个类,但可以实现多个接口
    • override修饰符表示重写
    • 可以有默认方法,若父类的默认方法冲突,则需要子类重写,使用super.xxx()调用某一父类方法
    interface Focusable {
        fun focus()
        fun show() = println("Focusable")
    }
    
    class Button : Clickable, Focusable {
        override fun click() {
            TODO("Not yet implemented")
        }
    
        override fun focus() {
            TODO("Not yet implemented")
        }
    
        override fun show() {
            super.show()
            super.show()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    接口中可以声明域,每个子类都要初始化接口中的域

    interface User {
        val name: String
    }
    
    class Person(override val name: String) : User
    
    class Man(val email: String) : User {
        override val name: String
            get() = email.substringBefore("@")
    }
    
    class WonMan() : User {
        override val name: String = "A"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    接口中也可以使用getter和setter,前提是不引用变量

    interface User {
        val email:String
        val name: String
        get() = email.substringBefore('@')
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    bean类

    只有数据没有其他代码的对象通常叫做值对象,如JavaBean

    public class Person {
        private String name;
    
        private final int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    使用过程如下

    Person person = new Person("java", 8);
    
    person.setName("kotlin");
    System.out.println(person.getName());
    System.out.println(person.getAge());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    将上述Java复制到代码.kt文件,会触发自动转换(.kt不要求类名和文件名一致,可将多个类放到同一文件,且文件名随意

    在这里插入图片描述

    转换后的代码如下,name为var变量(默认带有getter和setter),而age为val变量(只有getter)

    class Person(
        var name: String,
        val age: Int
    ) 
    
    • 1
    • 2
    • 3
    • 4

    使用方法如下

    val person = Person("java", 8);
    
    person.name = "kotlin"
    println(person.name)
    println(person.age)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果一个属性可以根据其他属性计算,可使用自定义getter

    class Rectangle(val height: Int, val width: Int) {
        val isSquare: Boolean
            get() {
                return height == width
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    类的重写

    类和方法默认都是final的,否则需要使用open修饰符

    open class Button {
        fun click() {}
        open fun focus() {}
    }
    
    open class MyButton : Button() {
        override fun focus() {
            super.focus()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    抽象类

    抽象方法默认为open,其他的可有可无

    abstract class Button {
        abstract fun click()
        open fun focus() {}
        fun press() {}
    }
    
    open class MyButton : Button() {
        override fun click() {
            TODO("Not yet implemented")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可见性

    Kotlin没有包的概念,internal为模块可见性

    在这里插入图片描述

    • private类在Java中会被编译成protect
    • internal类或域在Java中会被编译成public

    嵌套类

    在一个类中声明另一个类

    • 在Java中,未用static声明的类为内部类(含有外部类的隐式引用),加上static的类为嵌套类(不含有外部类的隐式引用)
    • 而Kotlin相反,未用inner声明的类为嵌套类(不含有外部类的隐式引用),加上inner的类为内部类(含有外部类的隐式引用)
    • 嵌套类不能访问外部类的实例,外部类不能访问嵌套类中的private域

    在这里插入图片描述

    如果需要使用内部类存储序列化信息,需要声明为static转为嵌套类,否则将无法序列化

    public class Button {
    
        private boolean isClickable;
    
        public ButtonState getCurrentState() {
            return new ButtonState(this.isClickable);
        }
    
        public void restoreState(ButtonState state) {
            this.isClickable = state.canClick;
        }
    
        public static class ButtonState implements Serializable {
            private boolean canClick;
    
            public ButtonState(boolean isClickable) {
                this.canClick = isClickable;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    而在Kotlin中正好相反,默认为加上static的嵌套类,如果需要成为内部类则应加上inner修饰符,使用this@Outer访问外部类实例

    class Button {
        private var isClickable = false
    
        val currentState: ButtonState
            get() = ButtonState(isClickable)
    
        fun restoreState(state: ButtonState) {
            isClickable = state.canClick
        }
    
        class ButtonState(val canClick: Boolean) : Serializable
    
        inner class TestState(val canClick: Boolean) {
            fun getOuterReference(): Button = this@Button
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    受限的类继承结构

    在如下结构和判断中,若新增了类,但却没有新增分支,会导致其调用走到else

    interface Expr
    class Num(val value: Int) : Expr
    class Sum(val left: Expr, val right: Expr) : Expr
    
    fun eval(e: Expr): Int =
        when (e) {
            is Num -> {
                e.value
            }
            is Sum -> {
                eval(e.left) + eval(e.right)
            }
            else -> {
                throw IllegalArgumentException("")
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    通过sealed修饰父类,将子类嵌套在父类,可避免额外的else分支,当新增子类时,when表达式会提示编译失败

    sealed 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.left) + eval(e.right)
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    主构造函数

    class+类名+[修饰符]+constructor()表示主构造函数,init表示初始化语句块

    class User private constructor(name: String) {
        val name: String
    
        init {
            this.name = name
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    若主构造函数没有注解或可见性修饰符,上述还可以省略为

    class User(name: String) {
        val name: String = name
    }
    
    • 1
    • 2
    • 3

    如果参数用相应的构造方法参数来初始化,还可以简化为

    class User(val name: String) 
    
    • 1

    也可以加上默认参数,若所有构造方法参数都有默认值,会生成一个额外的不带参数的构造方法来使用所有的默认值

    class User(val name: String = "A")
    
    • 1

    若类有父类,可在继承列表中调用父类构造函数初始化父类,如下传入Person的参数name会被用于构造User

    open class User(val name: String)
    
    class Person(name: String) : User(name) {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    从构造函数

    当需要多个构造函数时,可使用从构造函数

    open class User {
        private val name: String
        private var age: Int = 0
    
        constructor(name: String) {
            this.name = name
        }
    
        constructor(name: String, age: Int) {
            this.name = name
            this.age = age
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可使用this和super调用自身及父类的构造从构造函数,若类没有主构造函数,那么每个从构造函数必须初始化父类

    open class User {
        private val name: String
        private var age: Int = 0
    
        constructor(name: String) {
            this.name = name
        }
    
        constructor(name: String, age: Int) {
            this.name = name
            this.age = age
        }
    }
    
    class Person : User {
        constructor(name: String) : this(name, 18) {
        }
    
        constructor(name: String, age: Int) : super(name, age) {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    通过setter/getter访问域的值

    在setter中可以通过标识符field读写域的值,getter中只能读

    class User(val name: String) {
        var address: String = "unspecified"
            set(value: String) {
                println(
                    """
                    |Address was changed for $name:
                    |"$field" -> "$name"
                """.trimMargin()
                )
                field = value
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    如上,在更新address值时

    val user = User("Tom")
    user.address = "A Street"
    
    • 1
    • 2

    额外打印一些信息

    Address was changed for Tom:
    "unspecified" -> "Tom"
    
    • 1
    • 2

    修改getter/setter可见性

    如下,使用内部计数字符个数,不能对其setter

    class lengthCounter {
        var counter: Int = 0
            private set
    
        fun addWord(word: String) {
            counter += word.length
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    data类

    data修饰符将会为类自动生成通用方法的实现,如toString()、equals()和hashCode(),会将主构造函数中申明的属性纳入考虑

    • ==默认调用equal()
    • ===才是比较引用
    data class Person(val name: String, val age: Int)
    
    • 1

    Kotlin还为data类新增了copy(),用于返回相同的对象,且互不影响

    val A = Person("A", 18)
    val B = A.copy();
    println(A)
    println(B)
    
    • 1
    • 2
    • 3
    • 4

    通过by实现类委托

    在新增对象功能时,原有功能通常会被委托给原对象,这样会出现大量样板代码

    class MyCollection : Collection {
        private val innerList = arrayListOf()
        override val size: Int
            get() = innerList.size
    
        override fun contains(element: T): Boolean {
            return innerList.contains(element)
        }
    
        override fun containsAll(elements: Collection): Boolean {
            return innerList.containsAll(elements)
        }
    
        override fun isEmpty(): Boolean {
            return innerList.isEmpty()
        }
    
        override fun iterator(): Iterator {
            return innerList.iterator()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    可通过 by 将接口的实现委托到另一个对象,编译器会自动实现上面的方法,也可自行重写方法

    class MyCollection(innerList: Collection = ArrayList()) : Collection by innerList {
    
    }
    
    • 1
    • 2
    • 3

    object关键字

    定义一个类并同上创建一个实例

    使用对象声明创建单例

    一个对象声明不允许有构造方法,在定义时即被创建,不需要再调用构造方法

    object Person {
        val name: String = ""
        fun printName() {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可使用对象.属性/方法来访问

    Person.name = "A"
    Person.printName()
    
    • 1
    • 2

    当实现一个接口但不包含任何状态时,通常使用对象声明,如实现一个比较器

    object CaseInsensitiveFileComparator : Comparator {
        override fun compare(p0: File, p1: File): Int {
            return p0.path.compareTo(p1.path, true)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在使用时可直接传递到接收Comparator的函数

    println(CaseInsensitiveFileComparator.compare(File("/User"), File("/user")))
    
    val files = listOf(File("/Z"), File("/a"))
    println(files.sortedWith(CaseInsensitiveFileComparator))
    
    • 1
    • 2
    • 3
    • 4

    在Java中被编译成通过静态字段持有单例,则通过如下调用

    CaseInsensitiveFileComparator.INSTANCE.compare(File("/User"), File("/user"));
    
    • 1

    伴生对象代替静态方法和字段

    创建工厂方法

    class User {
        val name: String
    
        constructor(email: String) {
            name = email.substringBefore('@')
        }
    
        constructor(id: Int) {
            name = id.toString()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    使用companion定义伴生对象,将上面改成工厂方法实现

    class User private constructor(val name: String) {
        companion object {
            fun newUserByEmail(email: String) = User(email.substringBefore('@'))
            fun newUserById(id: Int) = User(id.toString())
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在使用时,可通过类名称访问对象的方法和属性

    val user1 = User.newUserByEmail("xxx@gmail.com")
    val user2 = User.newUserById(123)
    
    • 1
    • 2

    伴生对象在Java中通过如下方式调用,若存在名字,则代替Companion

    User.Companion.newUserById(123)
    
    • 1

    实现接口

    伴生对象也可实现接口

    interface Factory {
        fun newUserByEmail(email: String): T
        fun newUserById(id: Int): T
    }
    
    class User(val name: String) {
        companion object : Factory {
            override fun newUserByEmail(email: String) = User(email.substringBefore('@'))
            override fun newUserById(id: Int) = User(id.toString())
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    对于如下方法

    fun  createUser(factory: Factory) {
    
    }
    
    • 1
    • 2
    • 3

    可直接将伴生对象所在类名字当作实现了该接口的对象实例来使用

    createUser(User)
    
    • 1

    扩展函数

    当需要定义通过类调用的方法时,可以通过伴生对象函数来实现

    class User(val name: String) {
        companion object {
    
        }
    }
    
    fun User.Companion.printName() {
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    使用如下

    User.printName()
    
    • 1

    对象表达式创建匿名对象

    object声明匿名对象代替Java的匿名内部类

    interface OnClickListener {
        fun onclick()
    }
    
    fun setOnClickListener(listener: OnClickListener) {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    对于上面常用的监听事件,可传入声明匿名对象

    setOnClickListener(object : OnClickListener {
        override fun onclick() {
        
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    若需要命名则提取出来,匿名对象可以实现多个接口

    val listener = object : OnClickListener {
        override fun onclick() {
        }
    }
    setOnClickListener(listener)
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    图书管理系统(C语言实现)
    南辕北辙
    物联网开发学习笔记——目录索引
    CentOS7.9安装kafka-3.2.0和window10 下安装kafka-3.2.0
    【英语:基础高阶_全场景覆盖表达】K11.口语主题陈述——事物类
    JavaScript 在 Promise.then 方法里返回新的 Promise
    NewStarCTF 2023 web
    边缘计算(Edge Computing)
    H3C交换机通过tftp自动备份配置
    Two answers to questions about MISC
  • 原文地址:https://blog.csdn.net/qq_35258036/article/details/134095404