Kotlin学习笔记(三)类与对象

类的声明

类的定义:类通过class关键字来声明,类声明由类名、类头(指定其类型参数、主 构造函数等)和由大括号包围的类体构成。类头和类体都是可选的; 如果一个类没有类体,可以省略花括号。类具有一个唯一的主构造函数,只需在类名后面写上参数,如果需要函数体,可以写在init块中。

//如果非抽象类没有声明任何主、次构造函数,会自动生成一个无参构造函数
class Student

class Student2 constructor(name:String,age: Int){
    
}

//如果主构造函数没有任何注解或可见性修饰符,可以省略constructor关键字。构造函数默认是public的,如需私有,自己添加private等可见性修饰符。
class Student3(name:String,age: Int){
    
}

class Student4(name: String,age: Int){
    init {
        //主构造函数体
    }
}

//如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数 用 this 关键字即可:
class Person(val name: String) {
    constructor(name: String, parent: Person) : this(name) {
          parent.children.add(this)
    }
}

类的实例化

//类的实例化不需要new关键字,kotlin中也没有这个关键字
val student1 = Student()
var student2 = Student("jack",18)

类继承

Kotlin中所有类都有一个共同的超类Any,要声明一个显式的超类型,我们把类型放到类头的冒号之后:

//kotlin中的非抽象类默认是不可继承的,如需继承,需要自己声明open
open class People

class Student: People()

方法也是默认为final,如需继承,必须加open,同时子类的方法必须加override,且子类中的方法可以再次被覆盖,除非声明为final:

open class Student():People(){
    final override fun name(){ }
}

属性覆盖与方法覆盖类似,每个声明的属性可以由初始化属性或者getter方法的属性覆盖:

open class People {
    open val x: Int get { …… }
}
//你也可以用一个 var 属性覆盖一个 val 属性,但反之则不行。因为一个val 属性本质上声明了一个 getter 方法,而将其覆盖为 var 只是在子类中额外声明一个setter 方法。
class Student : People() {
    override val x: Int = ……
}

对于多继承来说,有时候会碰到超类中有同名函数,可以利用super来指明我们继承的是哪个超类:

open class A {
    open fun f() { print("A") }
    fun a() { print("a") }
}
interface B {
    fun f() { print("B") } // 接口成员默认就是“open”的
    fun b() { print("b") }
}
class C() : A(), B {
    // 编译器要求覆盖 f():
    override fun f() {
        super<A>.f() // 调用 A.f()
        super<B>.f() // 调用 B.f()
    }
}

可以用抽象成员覆盖一个非抽象的开放成员。

属性

声明属性的完整语法:

  var <propertyName> : [<propertyType>] [ = <property_init>]
                    [<getter>]
                    [<setter>]

其中初始值和getter、setter都是可选的,如果可以从初始值或者getter返回值中推断出类型,则属性类型也是可以省略的。上面定义的是一个可变属性,也可以用val来定义一个只读属性,只读属性不允许有setter方法。
  如果需要在getter和setter中访问这个属性自身的值,它需要创建一个 backing field 。可以使用 field 这个预留字段来访问,它会在属性初始化时自动创建。需要注意的是,如果我们直接调用了属性,那我们会使用setter和getter而不是直接访问这个属性。 backing field 只能在属性访问器内访问。

    
    var name: String = "jack"
    var age: Int = 18
        set(value) {
            //age = value//注意这种方式会触发死循环,无限调用set,最终崩溃
            field = value
        }
    val sex = "男"
    val address
        get() = "长沙"
    var score: Int = 88
        get() = 22 //无论什么时候调用score,得到的都会是18
        set(value) {
            field = value
        }
...
    val student = Student()
    println(student.age)//18
    println(student.score)//22
    student.age = 20
    student.score = 99
    println(student.age)//20
    println(student.score)//22

编译期常量

已知值的属性可以使用 const 修饰符标记为编译期常量,类似于java中的static final(java中的static final修饰的属性在调用时不需要将类初始化,仅仅static修饰的值调用是需要进行类的初始化的)。 这些属性需要满足以下要求:

  • 位于顶层(即包级属性,不能写在类中)或者是 object 的一个成员
  • 用 String 或原生类型值初始化
  • 没有自定义 getter
const val pai = 3.1415926
object Per{
    const val sex = "男"
}
fun main(args: Array<String>) {
    println(pai)//3.415926
    println(sex)//男
}

惰性初始化属性

一般地,属性声明为非空类型必须在构造函数中初始化。但这有时候很不方便,比如我想声明一个数据集合为非空,但在初始化时我们又得不到这个数据,可以使用lateinit来修饰变量,延迟初始化。注意lateinit不能修饰val,只能修饰非空可变变量,并且该变量不能为原始类型,不能不能有自定义getter或setter。

class Teacher {
    var name = "YoYo"
    var age: Int

    init {
        age = 25
    }

    lateinit var students: List<Student>
}

对象

对象表达式

kotlin中的对象表达式类似于java的匿名内部类。object表达式可以直接访问包含这个表达式的作用域的变量,而不用像java一样需要加final修饰。

interface Pl2303 {
    fun connectSuccess()
}

class Pl2303Activity {
    val s = object {//没有超类
        val i = 1
    }

    fun addListener(pl2303: Pl2303) {}

    fun connect() {
        addListener(object : Pl2303 {//有超类
            override fun connectSuccess() {

            }
        })
    }
}

请注意,匿名对象可以用作只在本地和私有作用域中声明的类型。如果你使用匿名对象作为公有函数的返回类型或者用作公有属性的类型,那么该函数或属性的实际类型会是匿名对象声明的超类型,如果你没有声明任何超类型,就会是 Any 。在匿名对象中添加的成员将无法访问,就像java中的匿名内部类,也只能在父类里面访问。

class C {
  // 私有函数,所以其返回类型是匿名对象类型
  private fun foo() = object {
    val x: String = "x"
  }
  // 公有函数,所以其返回类型是 Any
  fun publicFoo() = object {
    val x: String = "x"
  }
  fun bar() {
    val x1 = foo().x // 没问题
    val x2 = publicFoo().x // 错误:未能解析的引用“x”
  }
}

对象声明

object A{//对象也可以有超类,跟在冒号后面即可,多个超类用逗号隔开
    fun a_1(){

    }
}

class B{
    fun b_1(){
        A.a_1()
    }
}

A是一个对象声明,跟在object关键字的后面,对象声明不是表达式,不能跟在等号的右边。可以直接通过对象名来调用。
  对象声明不能在局部作用域(即直接嵌套在函数内部),但是它们可以嵌套到其他对象声明或非内部类中。

伴生对象

与Java不同,Kotlin中并没有静态方法的概念。如需使用,可以用companion object(其实可以简单看成一个对象声明,默认名为Companion)。

class Chess {

    companion object {//省略了对象名Companion
        val WHITE_MAX: Double = 100.0
        val BLACK_MAX: Double = 100.0
        val WHITE_MIN: Double = -100.0
        val BLACK_MIN: Double = -100.0
        val RAW_NUM = 19
        val TYPE_QUICK_CHECK: String = "~DEB1#"
        val GET_ALL_DATA_IN_QUICK_MODE: String = "~REQ#"
    }

    ....
    //调用方式:
    Student.id//kotlin中调用
    Student.Companion.getId()//java中调用,Companion为默认的伴生对象名,也可以通过companion object 后跟一个对象名来改变。
}

三者区别:

  • 对象表达式是在使用他们的地方立即执行(及初始化)的
  • 对象声明是在第一次被访问到时延迟初始化的
  • 伴生对象的初始化是在相应的类被加载(解析)时,与 Java 静态初始化器的语义相匹配
      第三条很好理解,主要看下前两条:
class B {
    init {
        println("BBBBBB")
    }
    val s = object {
        init {
            println("ssssss")
        }
        fun p(){
            print("pppppp")
        }
    }
    object A {
        init {
            println("AAAAAAA")
        }
        fun a_1() {
            println("aaaaaaa")
        }
    }

    fun b_1() {
        A.a_1()
    }
}


fun main(args: Array<String>) {
    val b = B()
    println("------------------")
    b.b_1()
}
//结果
//BBBBBB
//ssssss
//------------------
//AAAAAAA
//aaaaaaa

说明在类初始化的时候,对象表达式身为属性已经被执行,而对象声明在被访问的时候才被执行。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容

  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,209评论 9 118
  • 面向对象编程(OOP) 在前面的章节中,我们学习了Kotlin的语言基础知识、类型系统、集合类以及泛型相关的知识。...
    Tenderness4阅读 4,444评论 1 6
  • Find the contiguous subarray within an array (containing ...
    matrxyz阅读 140评论 0 0
  • 致每一个中秋不能回家的游子 你那里的春与秋 胜过我见过爱过的 一切山川河流 我从不避讳,说自己是个乡下孩子。在乡间...
    塬上百姓阅读 518评论 0 2
  • 空濛不是雨, 氤氲天地湿。 朔风从北斗, 铿然碎玉生。 飘零何限恨? 触温即消溶。 轮回天地间, 悠扬一场梦。
    江南信阅读 114评论 0 1