Kotlin类与对象

如果描述的类型不是基本类型怎么办?

  • 任何复杂的数据类型都是由基本的数据类型构成的

面向对象

  • 使用基本数据类型描述复杂的事物

例如:使用面向对象的方式来描述用户

class User(var id:Int, var name:String)

fun main(args:Array<String>) {
    val user = User(1, "alice")
    println("user id is ${user.id}")
    println("user name is ${user.name}")
}

  • Kotlin类与Java类基本类似,类是拥有共同属性的对象蓝图。
  • Kotlin类使用关键字class声明并后跟类名
  • Kotlin类具有一个类头(class header),类头可以指定类型参数、构造函数等、类体。
  • Kotlin类可以包含构造函数、初始化代码块、函数、属性、内部类、对象声明

Kotlin中类与接口和Java相比是有些区别的

  • Kotlin中接口可以包含属性声明
  • Kotlin的类申明默认是finalpublic
  • Kotlin的嵌套类并不是默认在内部的,它们不包含外部类的隐私引用。
  • Kotlin的构造函数分为主构造函数和次构造函数
  • Kotlin中可以使用data关键字来申明一个数据类
  • Kotlin中可以使用object关键字来表示单例对象、伴生对象等

在Kotlin中任何一个非抽象类默认都是不可以被继承的,这相当于Java中给类声明了final关键字。之所以这样设计其实和val关键字原因是差不多的。因为类和变量一样,最好都是不可变的,如果一个类允许被继承的话,是无法预知子类会如何实现,因此可能会存在一些未知的风险。

抽象类本身是无法创建实例的,一定要由子类去继承才能创建实例,因此抽象类必须可以被继承才行,要不然也就没有意义。

如果一个类不是专门为继承而设计的,那么就应该主动将它加上final声明,禁止它可以被继承。

对象

  • 对象是实时实体,是拥有状态和行为的逻辑实体。
  • 对象具有状态用来表示对象的值
  • 对象拥有行为用来表示对象的函数
  • 对象用于访问类的属性和成员函数
  • Kotlin允许创建一个类的多个对象

创建对象

创建一个对象,Kotlin分为两步走,第一步是创建引用,第二步是创建对象。

var obj = className()

访问类属性和成员函数

对象可以通过点运算符.访问类的属性和成员函数

obj.id

类的声明

声明一个类的时候,至少需要包含class关键字以及后面的类名。可以根据需要添加类头(用来声明参数)和类体。

  • 创建空的构造函数,由编译器自动生成。
class className{

}
  • 如果一个类没有类体则可以省略花括号
//无参类定义
class EmptyClass

fun main(args:Array<String>){
    //实例化对象
    var ec = EmptyClass()

    //调用类的toString()方法
    println(ec.toString())//EmptyClass@6e8cf4c6

    //::表示将类传入一个方法中作为参数
    println(ec::class)//class EmptyClass

    //使用is判断类的归属
    println(ec is EmptyClass)//true
}
  • 为类提供构造函数则需添加一个构造函数关键字constructor并后跟类名
//带参类定义
class User{
    //属性定义
    var id:Int = 0
    var name:String = ""
    //构造函数
    constructor(id:Int, name:String){
        this.id = id
        this.name = name
    }
    //复写方法
    override fun toString():String{
        return "User(id=$id, name='$name')"
    }
}

fun main(args:Array<String>){
    var user = User(1, "junchow")
    println(user.toString())//User(id=1, name='junchow')
}

在声明类的同时声明构造函数

//声明类同时声明构造函数
class User(id:Int, name:String){
    //属性定义
    var id:Int = id
    var name:String = name
    //复写方法
    override fun toString():String{
        return "User(id=$id, name='$name')"
    }
}

fun main(args:Array<String>){
    var user = User(1, "junchow")
    println(user.toString())//User(id=1, name='junchow')
}

声明类时不创建是属性可在构造参数前添加var关键字

//声明类同时声明构造函数,在构造参数前添加var后可不创建对应属性。
class User(var id:Int, var name:String){
    //复写方法
    override fun toString():String{
        return "User(id=$id, name='$name')"
    }
}

fun main(args:Array<String>){
    var user = User(1, "junchow")
    println(user.toString())//User(id=1, name='junchow')
}

属性和行为

  • 类具有静态地属性和动态的行为
  • 对象同样具有行为和属性

属性字段

类的属性可以使用关键字var声明为可变类型,也可以使用只读关键字val声明不可变类型。val不允许设置setter函数,因为它是只读的。

  • var可变属性
var id:Int? // 错误:需初始化默认实现了getter和setter方法
var id = 0 //类型为Int,默认实现了getter和setter方法
  • val只读属性:只读属性禁止设置setter函数
val id:Int? //类型为Int,默认实现了getter但必须在构造函数中初始化。
val id = 0 //类型为Int默认实现了getter

属性声明的完整语法

var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]

属性的初始化器property_initializergettersetter都是可选的,

属性类型propertyType如果可以从初始化器property_initializer或从getter返回值中推断出来,也可以省略的。

var initialized = 1 // 类型为Int 默认getter

Kotlin默认为属性实现了get/set方法但可以被重写,用来修改访问权限。

gettersetter是可选则,如果属性类型可以从初始化语句或类的成员函数中推断出来就可以省略掉类型。

幕后字段

Kotlin类没有字段,Kotlin提供了Backing Fields后端变量机制,备用字段使用field关键字声明,field关键字只能用于属性的访问器。

class User{
    var id:Int = 0
        get() = field
        set(value){
            if(value < 100){
                field = value
            }else{
                field = -1
            }
        }
    var name:String = ""
        get() = field.toUpperCase()
        set
    var no:String = ""
        private set
    //复写方法
    override fun toString():String{
        return "User(id=$id, name='$name')"
    }
}

fun main(args:Array<String>){
    var user:User = User(    )
    user.id = 1000
    user.name = "junchow"
    println(user.toString())//User(id=-1, name='JUNCHOW')
}

可以像使用普通函数一样使用构造函数来创建类的实例

val user = User()

Kotlin中是没有new关键字的,对象如果需要使用属性,可直接使用点号.引用即可。

println(user.id)
println(user.name)

Kotlin中属性必须初始化或声明为抽象

class User{
  var id:Int = 0
  var name:String? = null
  var salary:Float = 0f
  fun create(){

  }
}

幕后属性backing property

编译器常量

延迟初始化属性

Kotlin中非空属性必须在定义时初始化,Kotlin提供了一种延迟初始化的方案,可使用lateinit关键字描述属性。

覆盖属性

委托属性

类修饰符

类的修饰符分为类属性修饰符classModifier和访问权限修饰符_accessModifer

类属性修饰符

  • abstract 抽象类
  • final 类不可继承,默认属性。
  • open 类可继承
  • enum 枚举类
  • annotaion 注解类

访问修饰符

  • private 仅在同一个文件中可见
  • protected 同一个文件或子类可见
  • public 所有调用的地方可见,默认。
  • internal 同一个模块中可见

构造函数

Kotlin与Java相同的是都可以以包含多个构造函数,不同的是Java中的构造函数是平等的,Kotlin却分为了两级主构造函数和次构造函数,主构造函数是包含在类头中的,跟在类名(和泛型声明)之后,需要在init()方法中实现额外的操作。次构造函数可以在函数体中实现所有的操作。

Kotlin中构造函数是一个类似于方法的代码块,声明构造函数的名称与类的名称相同,后跟括号()。构造函数用于在创建对象时初始化变量。

  • Kotlin构造函数分为两种类型,分别是主构造函数和次构造函数。
  • Kotlin中的类可以有0~1个主构造函数和0~n个次构造函数

主构造函数

  • 主构造函数用于初始化
  • 主构造函数在类标题中声明
  • 主构造函数代码由带有可选参数的括号包裹
  • 主构造函数是类头的一部分,位于类名后。
  • 主构造函数中传入的参数可以在类体中为其赋值,也可以直接在主构造函数中声明。
  • 无论有没有声明主构造函数,Kotlin都会提供以一个构造函数,若不希望将某个类的构造函数暴露,需要对主构造函数进行处理。

每个类默认都会拥有一个无参构造函数,可以显式地为其指定参数。主构造函数的特点是没有函数体,直接定义在类名后面。如果想要在主构造函数中编写编写,则需要使用init结构体,可以将主构造函数的逻辑写到init模块中。

主构造函数直接跟在类名后面,主构造函数中声明的属性可以是可变的var也可以是不可变的val的,如果主构造函数没有任何注解或 可见性修饰符,则可省略constructor关键字。类默认是继承Any的,是可以省略的。

open class User constructor(var id:Int, var name:String):Any(){

}

open class User(var id:Int, var name:String){

}

如果主构造函数没有任何注解或可见性修饰符则可省略constructor关键字。

class User(id:Int, name:String){

}

如果类具有注解或可见性修饰符则constructor关键必不可少

class Member @Autoware public constructor(id:Int, name:String):User(id, name){

}

初始化块的主构造函数

  • 主构造函数中不能够包含任何代码,初始化程序块用来初始化代码。
  • 初始化代码可以放在以init关键字为前缀的初始化模块中。当类被实例化期间,初始化块会按照它们出现在类体中的顺序执行,并与属性初始化器交织在一起。
  • 初始化块的执行顺序与在类体中出现的顺序相同

例如:使用初始化块重写类

class User(id:Int, name:String){
  var username:String
  var nickname:String
  init{
    username = name.capitalize()
    nickname = name.id
  }
}

声明属性以及与主构造函数初始化属性的简洁语法

class User(val id:Int, var name:String){

}

与普通属性一样,主构造函数中声明的属性可以是可变var或只读val的。

如果构造函数有注解或可见性修饰符时constructor关键字则是必须的

class User public @Inject constructor(id:Int, name:String){

}

主构造器不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用init关键字作为前缀 。

class User constructor(id:Int, name:String){
    init{
        println("id is $id name is $name")
    }
}

fun main(args:Array<String>){
    var user:User = User(1, "junchow")//id is 1 name is junchow
}

主构造器的参数可以在初始化代码段中使用,也可以在类体定义的属性初始化代码中使用。比较简洁的方式是通过主构造器来定义属性并初始化属性值。

class User(id:Int = 0, name:String = ""){
    init{
        println("id is $id name is $name")
    }
}

fun main(args:Array<String>){
    var user:User = User()//id is 0 name is 
}

如果主构造器有注解或有可见修饰符,此时constructor关键字是必须的,注解和修饰符需要放在它之前。

私有主构造函数

如果不希望类被实例化可为类添加private修饰符

class User private constructor()

fun main(args:Array<String>) {
    //Error:(4, 21) Kotlin: Cannot access '<init>': it is private in 'User'
    val user:User = User()
}

可通过次构造函数来构建

次构造函数

  • 次构造函数又称之为辅助构造函数
  • Kotlin在类中可以创建一个或多个辅助构造函数
  • Kotlin类中使用关键字constructor创建辅助构造函数
  • 次构造函数必须包含constructor

类可以声明前缀为constructor的次级构造函数,次级构造函数是类的二级构造函数,需要添加前缀constructor

class User{
  constructor(id:Int){
  
  }
}

若类有主构造函数,每个次构造函数都需直接或间接的通过另一个次构造函数代理主构造函数,在同一个类中代理另一个构造函数需要使用this关键字。

如果一个非抽象类没有声明构造函数,它会产生一个没有参数的构造函数,构造函数是public。如果不希望类有公共的构造函数则需要声明一个空的主构造函数。

在JVM中如果主构造函数的所有参数都有默认值,编译器会生成一个附加的无参构造函数,这个构造函数会直接使用默认值。这使得Kotlin可以更加简单的使用如Jackson或JPA等需要使用无参构造函数来构建实例的库。

抽象类

嵌套类

内部类

匿名内部类

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

推荐阅读更多精彩内容