1. 类
Kotlin同java一样也是一门面向对象的语言
类成员
- 构造函数和初始化代码块
- 函数
- 属性
- 内部类
- 对象声明
a. 特点
- 构造函数有主次之分
- 不需要new关键字
- 所有类都有一个共同的超类Any
- 默认情况下所有类都是final不可继承,想要被继承就要用到open、abstract关键字
- 类的方法和属性同意被final关键字限制不能被继承和重写
- 被继承的属性或方法必须加override关键字,并且父类里面要声明为open,但借口就不需要声明
- Kotlin中的类如果是空类,没有任何语句,则可以省略大括号
- 类不存在在static方法,如果需要就要用到伴生对象
- 接口与Java8类似,既包含抽象方法的声明,也包含实现
- Kotlin中由四个可见性修饰符:private、protected、interal和public
- Kotlin在不继承类的情况下支持扩展函数和扩展属性
- 不光有枚举,还有数据类,密封类
- 支持类委托,事实证明类委托是继承以外更好的实现方式
b. 类的修饰符
属性修饰符
修饰符 | 作用 |
---|---|
annatation | 注解类 |
abstrat | 抽象类 |
final | 类不可继承,默认属性 |
enum | 枚举类 |
open | 类可继承,类默认是final的 |
Kotlin中类默认为final的,也不可继承的。如果需要继承,需要声明为open,或者abstract
c. 类的声明
class Invoice{
}
类的声明包含类名,类头(指定类型参数,主构造函数等等),以及类主体,用大括号包裹。类头和类体是可选的;
如果没有类体可以省略大括号
//没有类体,Empty是类名
class Empty
c.构造方法
在Kotlin中类可以有一个主构造函数以及多个二级构造函数。主构造函数是类头的一部分:跟在类名后面
注意
主构造函数不能包含任意代码,初始化代码可以放在以init做前缀的初始化块内
主构造函数的参数可以用在初始化块内,也可以用在类的属性初始化声明处
因为kotlin中的类定义同时也定义构造函数,这个时候是不能进行操作的,所以Kotlin增加了一个新的关键字init用来处理类的初始化问题,init模块中的内容可以直接使用构造函数的参数。
主构造方法必须写在类头,有且只有一个主构造方法
例1:
这个类定义一个 String 类型的成员变量 name,然后定义了带有一个 String 类型参数的构造函数,这个构造函数把参数列表中的 name 赋给了成员变量 name。
//用constructor关键字定义带有一个String类型参数的主构造函数
class Person constructor(name:String){
val name:String
init{ //使用init关键字定义主构造函数的行为
this.name = name
}
}
简化
//简化
class Person(val name:Stirng)
例2:
如果一个非抽象类没有声明(主或次)构造函数,他会有一个公有的不带参数的主构造函数。构造函数的可见性是public。如果不希望有一个工友构造函数,需要声明一个带有非默认可见性的空的主构造函数
class Ditreatme private constructor(){
}
例3:
如果构造函数有注解或可见性修饰符,这个constructor关键字是必须的,并且这些修饰符在他前面
class Customer public @Inject constructor(name:String){
....
}
主构造的参数可以在初始化块中使用。他们也可以在类体内声明的属性初始化器中使用:
class Customer(name:String){
val customerKey = name.toUpperCase()
}
contructor(name:String ="",age:Int = 12,sex : Int = 0):this(name){
}
次构造函数写在类语句中,可以有多个,也可以没有
例1:
声明前缀有constructor的次构造函数
class Person {
constructor(parent: Person) {
}
}
例2:
如果类有一个主构造函数,每个次构造函数都需要委托给主构造函数,可以直接委托或者通过别的次构造函数间接委托,委托到同一个类的另一个构造函数用This关键字即可
class Person(val name:String){
var old:Int =0
constructor(name:String,parent:Person,old:Int)
:this(name){
//this(name)就是委托代码
parent.children.add(this)
this.old = old
}
}
例3:
次构造函数不能再参数列表中声明并初始化成员变量,还是上面的代码,“old:int”前面为什么没有val,因为次构造函数会改动old的值,所以old必须声明为var
扩展
如果一个非抽象类没有声明构造函数(主构造函数或二级构造函数),他会生成一个没有参数的构造函数。该构造函数的可见性是public。如果 你不想你的泪有公共的构造函数,你就声明一个拥有非默认可见性的空主构造函数:
class DontCreateMe privates constructor(){
}
注:
在 JVM 虚拟机中,如果主构造函数的所有参数都有默认值,编译器会生成一个附加的无参的构造函数,这个构造函数会直接使用默认值。这使得 Kotlin 可以更简单的使用像 Jackson 或者 JPA 这样使用无参构造函数来创建类实例的库。
class Customer(val constomerName:String = "")
d.创建类的实例
使用普通函数那样使用构造函数创建类的实例
val invoice = Invoice()
val customer =Customer("ddd")
注意:
Kotlin没有new 关键字
嵌套类、内部类或匿名类的实例
嵌套和内部类
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo = Outer.Nested().foo() // == 2
2.继承
Kotlin中所有的类都有共同的父类Any,他是一个没有父类声明的类的默认父类
1.继承的基本使用
在Kotlin中,类默认是final类型,也就是不可继承的,需要显示的将类声明为open。Kotlin使用冒号代替java中的extend来表示继承关系,如果父类的构造函数有参数,这在继承时也要注明:
open class ParentClass(name:String){
}
class chile(name:String,age:Int):ParentClass(name){
}
如果类有主构造函数,继承类可以而且必须在主构造函数中使用参数立即初始化。
如果类有主构造函数,这必须在每一个构造方法中庸super关键字初始化基类,或者在代理类一个函数做这件事。注意在这种情况中不同的二级构造函数可以调用基类不同的构造方法
2.多构造函数的继承
次级构造函数初始化使用super关键字,类似于java中的super
class Childe:ParentClass{
constructir(name:String):super(name){
log("child")
}
construstor(name:String,age:Int):super(name){
}
}
3.方法重载
秉持多用组合、少用继承的原则,类中的方法默认也是final的,父类如果先要某个方法可以被自雷重载,那么这个方法也要声明为open;而子类如果要重载父类的方法,这方法必须使用override关键字
open class ParentClass(name:String){
open fun publicMethod(){
log("I am public")
}
}
class Child(name:String,age:Int):ParentClass(name){
override fun publicMethod(){
super.publicMethod()
}
}
成员标记为override的本事是开放的。如果想禁止重写,使用final关键字
class child(name:String,age:Int):ParentClass(name){
final override fun publicMethod(){
}
}
4.同名方法
类实现了多个接口,其中某些接口中恰好有相同的方法。Kotlin中使用尖括号对父类进行标记
open class ParentClass(name:String){
open fun publicMethod(){
}
}
interface ParentInterface(name:String){
fun publicMethod(){
}
}
class Child(name:String,age:Int):ParentClass(name),ParentInterface{
override fun publicMethod{
super<PerentClass>.publicMethod()
super<ParentInterface>.publicMethod()
}
}
5.复写属性
复写属性与复写方法类似,在一个父类声明的属性在子类上被重新声明,必须添加override,并且他们必须具有兼容的类型。每个被声明的属性都可以被一个带有初始化器的属性或带有getter方法的属性覆盖
open class Person{
open val x:Int get{....}
}
class chrild:Person(){
override val x:Int = ...
}
还可以用var属性覆盖一个val属性,但反之则不允许。这是允许的,因为val属性本职上声明了一个getter方法,并将其重写给var,另外在派生勒种声明了setter方法。
注意,可以在主构造函数中使用override关键字作为属性声明的一部分。
interface Person{
val count:Int
}
class Bar1(override val count:Int):Foo
class Bar2 :Foo{
override var count:Int = 0
}
//简化
class Person(val name:Stirng)
注意:
1.如果主构造函数没有任何修饰符,这可以去掉construtor关键字
2.如果主构造函数中定义的参数使用val(只读)或者var(读写)修饰,则会创建与这个参数同名的成员变量,并使用传入的参数值初始化这个成员变量
例2:
如果一个非抽象类没有声明(主或次)构造函数,他会有一个公有的不带参数的主构造函数。构造函数的可见性是public。如果不希望有一个工友构造函数,需要声明一个带有非默认可见性的空的主构造函数
class Customer public @Inject constructor(name:String){
....
}
例4:
主构造的参数可以在初始化块中使用。他们也可以在类体内声明的属性初始化器中使用:
class Customer(name:String){
val customerKey = name.toUpperCase()
}
例5:
参数可以设置默认值
contructor(name:String ="",age:Int = 12,sex : Int = 0):this(name){
}
次构造函数写在类语句中,可以有多个,也可以没有
例1:
声明前缀有constructor的次构造函数
class Person {
constructor(parent: Person) {
}
}
例2:
如果类有一个主构造函数,每个次构造函数都需要委托给主构造函数,可以直接委托或者通过别的次构造函数间接委托,委托到同一个类的另一个构造函数用This关键字即可
class Person(val name:String){
var old:Int =0
constructor(name:String,parent:Person,old:Int)
:this(name){
//this(name)就是委托代码
parent.children.add(this)
this.old = old
}
}
例3:
次构造函数不能再参数列表中声明并初始化成员变量,还是上面的代码,“old:int”前面为什么没有val,因为次构造函数会改动old的值,所以old必须声明为var
扩展
如果一个非抽象类没有声明构造函数(主构造函数或二级构造函数),他会生成一个没有参数的构造函数。该构造函数的可见性是public。如果 你不想你的泪有公共的构造函数,你就声明一个拥有非默认可见性的空主构造函数:
class DontCreateMe privates constructor(){
}
注:
在 JVM 虚拟机中,如果主构造函数的所有参数都有默认值,编译器会生成一个附加的无参的构造函数,这个构造函数会直接使用默认值。这使得 Kotlin 可以更简单的使用像 Jackson 或者 JPA 这样使用无参构造函数来创建类实例的库。
class Customer(val constomerName:String = "")
d.创建类的实例
使用普通函数那样使用构造函数创建类的实例
val invoice = Invoice()
val customer =Customer("ddd")
注意:
Kotlin没有new 关键字
嵌套类、内部类或匿名类的实例
嵌套和内部类
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo = Outer.Nested().foo() // == 2
open class ParentClass(name:String){
}
class chile(name:String,age:Int):ParentClass(name){
}
如果类有主构造函数,继承类可以而且必须在主构造函数中使用参数立即初始化。
如果类有主构造函数,这必须在每一个构造方法中庸super关键字初始化基类,或者在代理类一个函数做这件事。注意在这种情况中不同的二级构造函数可以调用基类不同的构造方法
2.多构造函数的继承
次级构造函数初始化使用super关键字,类似于java中的super
class Childe:ParentClass{
constructir(name:String):super(name){
log("child")
}
construstor(name:String,age:Int):super(name){
}
}
3.方法重载
秉持多用组合、少用继承的原则,类中的方法默认也是final的,父类如果先要某个方法可以被自雷重载,那么这个方法也要声明为open;而子类如果要重载父类的方法,这方法必须使用override关键字
open class ParentClass(name:String){
open fun publicMethod(){
log("I am public")
}
}
class Child(name:String,age:Int):ParentClass(name){
override fun publicMethod(){
super.publicMethod()
}
}
成员标记为override的本事是开放的。如果想禁止重写,使用final关键字
class child(name:String,age:Int):ParentClass(name){
final override fun publicMethod(){
}
}
4.同名方法
类实现了多个接口,其中某些接口中恰好有相同的方法。Kotlin中使用尖括号对父类进行标记
open class ParentClass(name:String){
open fun publicMethod(){
}
}
interface ParentInterface(name:String){
fun publicMethod(){
}
}
class Child(name:String,age:Int):ParentClass(name),ParentInterface{
override fun publicMethod{
super<PerentClass>.publicMethod()
super<ParentInterface>.publicMethod()
}
}
5.复写属性
复写属性与复写方法类似,在一个父类声明的属性在子类上被重新声明,必须添加override,并且他们必须具有兼容的类型。每个被声明的属性都可以被一个带有初始化器的属性或带有getter方法的属性覆盖
open class Person{
open val x:Int get{....}
}
class chrild:Person(){
override val x:Int = ...
}
还可以用var属性覆盖一个val属性,但反之则不允许。这是允许的,因为val属性本职上声明了一个getter方法,并将其重写给var,另外在派生勒种声明了setter方法。
注意,可以在主构造函数中使用override关键字作为属性声明的一部分。
interface Person{
val count:Int
}
class Bar1(override val count:Int):Foo
class Bar2 :Foo{
override var count:Int = 0
}