美文网首页Kotlin TutorialsAndroid开发Android技术知识
[Kotlin Tutorials 3] Kotlin中的类和对

[Kotlin Tutorials 3] Kotlin中的类和对

作者: 圣骑士wind | 来源:发表于2019-12-30 13:51 被阅读0次

    Kotlin中的类和对象

    Kotlin中的类关键字仍然是class, 但是创建类的实例不需要new.

    构造函数

    构造函数分为: primary constructor(一个)和secondary constructor(一个或多个).

    如果一个非抽象类自己没有声明任何构造器, 它将会生成一个无参数的主构造, 可见性为public.

    主构造 Primary Constructor

    Primary constructor写在类名后面, 作为class header:

    class Person constructor(firstName: String) {
    //... 
    }
    

    如果没有注解和可见性修饰符, 关键字constructor是可以省略的:

    class Person2(firstName: String) {
    //... 
    }
    

    init代码块和属性初始化代码

    Primary constructor是不能包含任何代码的, 如果需要初始化的代码, 可以放在init块中.

    在实例的初始化阶段, init块和属性初始化的执行顺序和它们在body中出现的顺序一致.

    class InitOrderDemo(name: String) {
        val firstProperty = "First property: $name".also(::println)
    
        init {
            println("First initializer block that prints $name")
        }
    
        val secondProperty = "Second property: ${name.length}".also(::println)
    
        init {
            println("Second initializer block that prints ${name.length}")
        }
    }
    

    这个类被实例化的时候, print的顺序和代码中的顺序一致.

    这个例子也可以看出, 主构造函数中传入的参数在init块和属性初始化代码中是可以直接使用的.

    实际上, 对于主构造中传入的参数是可以直接声明为属性并初始化的.

    class PersonWithProperties(val firstName: String, val lastName: String, var age: Int) {
    //    ... 
    }
    

    可以是val也可以是var.

    次构造 Secondary Constructors

    次构造是用constructor关键字标记, 写在body里的构造.

    如果有主构造, 次构造需要代理到主构造, 用this关键字.

    注意init块是和主构造关联的, 所以会在次构造代码之前执行.

    即便没有声明主构造, 这个代理也是隐式发生的, 所以init块仍然会执行.

    class Constructors {
        init {
            println("Init block")
        }
    
        constructor(i: Int) {
            println("Constructor")
        }
    }
    

    创建实例, 输出:

    Init block
    Constructor
    

    继承

    继承用:.
    方法覆写的时候override关键字是必须的.

    Kotlin中默认是不鼓励继承的(类和方法默认都是final的):

    • 类需要显式声明为open才可以被继承.
    • 方法要显式声明为open才可以被覆写.

    抽象类(abstract)默认是open的.
    一个已经标记为override的方法是open的, 如果想要禁止它被进一步覆写, 可以在前面加上final.

    属性也可以被覆盖, 同方法类似, 基类需要标记open, 子类标记override.

    注意val可以被覆写成var, 但是反过来却不行.

    interface Foo {
        val count: Int
    }
    
    class Bar1(override val count: Int) : Foo
    
    class Bar2 : Foo {
        override var count: Int = 0
    }
    

    注意, 由于初始化顺序问题. 在基类的构造, init块和属性初始化中, 不要使用open的成员.

    对象表达式和对象声明

    Java中的匿名内部类, 在Kotlin中用对象表达式(expression)和对象声明(declaration).

    表达式和声明的区别:
    声明不可以被用在=的右边.

    对象表达式 object expression

    objec关键字可以创建一个匿名类的对象.

    这个匿名类可以继承一个或多个基类:

    open class A(x: Int) {
        public open val y: Int = x
    }
    
    interface B {
    // ...
    }
    
    val ab: A = object : A(1), B {
        override val y = 15
    }
    

    也可以没有基类:

    fun foo() {
        val adHoc = object {
            var x: Int = 0
            var y: Int = 0
        }
        print(adHoc.x + adHoc.y)
    }
    

    对象声明 object declaration

    在Kotlin中可以用object来声明一个单例.

    // singleton
    object DataProviderManager {
        fun doSomething() {
        }
    }
    
    fun main() {
        DataProviderManager.doSomething()
    }
    

    对象声明的初始化是线程安全的.
    使用的时候直接用类名即可调用它的方法.

    伴生对象 Companion Objects

    在类里面写的对象声明可以用companion关键字标记, 表示伴生对象.

    Kotlin中的类并没有静态方法.
    在大多数情况下, 推荐使用包下的方法.

    如果在类中声明一个伴生对象, 就可以像Java中的静态方法一样, 用类名.方法名调用方法.

    class MyClass {
        companion object Factory {
            fun create(): MyClass = MyClass()
        }
    }
    
    fun main() {
        MyClass.create()
    }
    

    Model类神器: data class

    model类可以用data来标记为data class.

    data class User(val name: String, val age: Int)
    

    编译器会根据在主构造中声明的所有属性, 自动生成需要的方法, 包括equals()/hashCode(), toString(), componentN()copy().

    为了让生成的代码有意义, 需要满足这些条件:

    • 主构造至少要有一个参数.
    • 所有的主构造参数都要被标记为valvar.
    • data class不能为abstract, open, sealed或inner.
    • Kotlin 1.1之前, 还只能实现接口. 1.1之后, 可以继承其他类了.

    注意:

    • 如果equals(), hashCode()toString()有显式的实现, 或基类有final版本, 这三个方法将不会被生成, 而使用现有版本.
    • 如果需要生成的类有无参数的构造器, 那么所有的属性都需要指定一个默认值.
    data class UserWithDefaults(val name: String = "", val age: Int = 0)
    
    • 生成的方法只会使用主构造中声明的属性, 如果想要排除某些属性, 可以放在body中声明.

    实践建议

    虽然data class中的属性可以被声明为valvar, 但是推荐使用val, 即不可变(immutable)的属性, 从而让类的实例是不可变的.

    不可变的实例在创建完成之后就不会再改变值.
    可以用copy()方法创建新的实例, 修改一些属性.

    参考

    相关文章

      网友评论

        本文标题:[Kotlin Tutorials 3] Kotlin中的类和对

        本文链接:https://www.haomeiwen.com/subject/jrfmoctx.html