Kotlin链接https://www.kotlincn.net/docs/reference/classes.html
类的定义
在Kotlin中使用关键字class定义
具体形式包括两种:
1. 带类体的定义:
class A{ // 类体 // 类A无主构造函数 }
2. 不带类体的定义: 一般很少用
class B
构造函数
主构造函数
Kotlin类可以有一个或没有主构造函数,主构造函数的定义放在类头上
标准的形式定义如下:
class A constructor(str: String){ //类体 //()内是主构造函数的参数 }
当主构造函数没有 访问修饰符的限制 也没有注解修饰(@...形式) 则可以省略 constructor 关键字:
class B(str:String){ // 类体 }
同时主构造函数的参数 既可以在类体的 属性初始化器 (var/val 变量=) 使用,也可以在 初始化块 init{...} 中使用,注意成员方法内不可用,成员方法内只能使用属性初始化器声明的变量。
class A(arg_name:String,arg_age:Int){ var name=arg_name // 默认权限均用public init { // 初始化块中既可以使用主构造函数的参变量也可使用属性初始化器的变量 println("running age:$name init_block") } var age=arg_age init { println("running age:$arg_age init_block") } fun print(){ //println(arg_name) arg_name 不存在 println("This is $name. Age is $age") } } fun main(args:Array<String>){ val hellen=A("Hellen",15) // 按从上到下的顺序依次执行 属性初始化器 和 初始化块 hellen.print() } //执行结果: //running age:Hellen init_block //running age:15 init_block //This is Hellen. Age is 15
属性初始化器和初始化块执行优先级相同,执行顺序从上到下依次执行。无论类是否有主构造函数,实例化类时都先执行属性初始化器和初始化块。
class B{ private val input=Scanner(System.`in`) init { print("please input name: ") } var name=input.next() init { print("please input age: ") } var age=input.nextInt() fun print(){ println("This is $name. Age is $age") } } fun main(args:Array<String>){ val peter=B() peter.print() } //运行结果 //please input name: Peter 这里输入了Peter //please input age: 18 这里输入了18 //This is Peter. Age is 18
与普通函数一样,主构造函数内的参数可以指定var 或 val,无论指定与否参数均既可传var也可传val,但是指定了var或val,则主构造函数内的参数即可视为类属性,不能定义重名的类属性。也就是说 指定var或val相当于属性初始化器与主构造函数结合
class A(name:String,age:Int){ val name=name var age=age init { println(this.name+" "+this.age) } } class B(var name:String,var age:Int){ //var name=name 报错 //var age=age 报错 init { println(this.name+" "+this.age) } } class C(val name:String,val age:Int){ //val name=name 报错 //val age=age 报错 init { println(this.name+" "+this.age) } } fun main(args:Array<String>){ var name1="Hellen" var age1=18 val name2="Peter" val age2=17 val a=A(name1,age1) //Hellen 18 val b=A(name2,age2) //Peter 17 val c=B(name1,age1) //Hellen 18 val d=B(name2,age2) //Peter 17 val e=C(name1,age1) //Hellen 18 }
次构造函数
Kotlin类中可以无次构造函数或者有一个或多个次构造函数
若类有主构造函数,则每个次构造函数必须直接或间接委托给主构造函数,委托到同一个类的另一个构造函数用 this 关键字即可;而当类无主构造函数时,这个委托也隐式存在。也就是说,次构造函数会先执行初始化块和属性构造器再执行次构造函数体
class A{ //无主构造函数 lateinit var name:String init { //println(this.age)//会报错 } var age:Int=0 init { println(this.age) } //无主构造函数,次构造函数隐式委托 constructor(name:String){ this.name=name } constructor(age:Int){ this.age=age } constructor(name:String,age:Int){ this.age=age this.name=name } } fun main(args:Array<String>){ //val a=A();//报错,没有无参的构造 var a=A("123") var b=A(12) var c=A("123",12) } //运行结果 // 0 // 0 // 0
class B(var name:String){ //有主构造函数 init { println(name) } var age=5 constructor(name: String,age:Int):this(name) { //直接委托 this.age = age } var sex="" constructor(name:String,age:Int,sex:String):this(name,age){ //间接委托 this.sex=sex } fun printB(){ println(this.name+" "+this.sex+" "+this.age) } } fun main(args:Array<String>){ val e=B("Hellen") e.printB() val f=B("Hellen",18) f.printB() val h=B("Hellen",18,"male") h.printB() } // 运行结果 // Hellen // Hellen 5 // Hellen // Hellen 18 // Hellen // Hellen 18 male
当类中既无主构造函数也无次构造函数,类会自动生成一个不带参数的主构造函数
类的实例化
var/val 变量=类(参数) //可以使用主构造或者次构造进行创建,当两种构造函数均无时,()内为空,自动生成一个无参主构造函数
继承
默认超类Any
在Kotlin中,所有的类都默认继承Any类,Any类有三个方法可重载,分别是toString()、equals()和hashcode()。
和Java在Intellij IDEA中的应用一样,Kotlin同样支持Alt Insert快捷键自动生成这三个函数的重载
同Java一样,Kotlin只允许继承一个类,但可以实现多个接口(接口中的方法属性默认为open)
继承类标志open
Kotlin中的类默认是 final 关键字限制的,表示不可以被继承
若用 open 定义类,则表示类可以被继承
class A{ } open class Square(var edge:Double){ fun Circumference(){ println("周长为:"+4*edge) } open fun Area(){ println("面积为:"+edge*edge) } } //class Cube(): A() {} //报错A不可继承 class Cube(edge: Double):Square(edge){ override fun Area() { println("面积为:"+6*edge*edge) } fun Volumn(){ println("体积为:"+edge*edge*edge) } } fun main(args:Array<String>){ val a=Square(4.5) a.Circumference() a.Area() val b=Cube(4.5) b.Circumference() b.Volumn() b.Area() } // 运行结果 // 周长为:18.0 // 面积为:20.25 // 周长为:18.0 // 体积为:91.125 // 面积为:121.5
定义格式
open class fatherClass(主构造函数参数){ // 类体 } class sonClass(主构造函数参数):fatherClass(父类构造函数要的参数){ }
派生类初始化块执行顺序
执行顺序为:先执行父类的初始化块和属性初始化器,再执行子类声明时所选择的父类构造函数体,再执行子类的初始化块和属性初始化器,最后执行所选择的子类构造函数
open class fatherClass(){ var name="" var age=0 constructor(name:String,age:Int):this(){ println("this is father's constructor ") this.name=name this.age=age } init { println("this is fatherClass") } override fun toString(): String { return "fatherClass(name='$name', age=$age)" } } class sonClass(name:String,age:Int,sex:String):fatherClass(name,age){ var sex=sex init { println("this is sonClass") } override fun toString(): String { return super.toString()+"\n"+"sonClass(sex='$sex')" //super.方法() } } fun main(args:Array<String>){ var a=sonClass("Hellen",18,"male") println(a) } // 运行结果 // this is fatherClass // this is father's constructor // this is sonClass // fatherClass(name='Hellen', age=18) // sonClass(sex='male')
调用父类的可访问属性或方法
子类可访问父类除private访问修饰的其他属性或方法
通过关键字super访问
super.变量名 访问属性
super.函数名 访问成员方法
覆盖规则
覆盖方法
方法默认同类一样为final不可覆盖,只有用open表示的方法才可覆盖
子类进行父类方法的覆盖一定要有override关键字
子类中不允许定义与父类中无open限制的相同的函数名的函数,也就是说,如果子类和父类中有函数同名,那么父类中的该函数一定有open限制,子类中该函数一定有override限制
如果想禁止子类的子类覆盖父类的某方法,只需要子类用final override覆盖该方法
open class A(){ open fun printA(){ //方法体 } } open class B() : A() { final override fun printA() { //方法体 } //后续B子类不可再覆盖A中的printA方法 }
覆盖属性
覆盖属性与覆盖方法类似
注意的是:
var修饰的属性不可覆盖为val修饰的属性,而val修饰的属性可覆盖为var修饰的属性。
这是因为允许继承类多方法(多了一个set属性方法),但不允许继承类少方法(少了一个set属性方法)
覆盖规则
如果一个类从它的直接父类继承相同属性或方法的多个实现, 它必须覆盖这个成员并提供其自己的实现(否则会有使用歧义,不知道用哪个父类的)。
使用 super<类型>. 属性/方法 表示使用哪个父类的属性或方法
open class A{ open fun printA(){ println("Hello A") } } interface B{ open fun printA(){ println("Hello B") } } class C:A(),B{ override fun printA() { super<A>.printA() super<B>.printA() } } fun main(args:Array<String>){ var a=C() a.printA() } // 运行结果 // Hello A // Hello B