如果描述的类型不是基本类型怎么办?
- 任何复杂的数据类型都是由基本的数据类型构成的
面向对象
- 使用基本数据类型描述复杂的事物
例如:使用面向对象的方式来描述用户
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的类申明默认是
final
和public
的 - 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_initializer
、getter
、setter
都是可选的,
属性类型propertyType
如果可以从初始化器property_initializer
或从getter
返回值中推断出来,也可以省略的。
var initialized = 1 // 类型为Int 默认getter
Kotlin默认为属性实现了get/set
方法但可以被重写,用来修改访问权限。
getter
和setter
是可选则,如果属性类型可以从初始化语句或类的成员函数中推断出来就可以省略掉类型。
幕后字段
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等需要使用无参构造函数来构建实例的库。