在上一章学习了Kotlin基础语法,以及简单介绍了下Kotlin,这一节将会接着前面的学习类和对象,在上一节基础上进阶学习
类
Kotlin中类和java中一样用class声明,一个类文件由类名,类头和由大括号包围的类体构成。类头和类体是可选的,如果没有类体大括号是可以不写的。
//一个空的类
class Empty
主构造函数
在Kotlin中一个类是有一个主构造函数的,这个主构造函数是类头的一部分,跟在类名后面
class Person constructor(firstName: String) {
}
如果在主构造函数中没有任何注解或者可见修饰符,可以不写构造函数关键字,有就必须写。
class Person(firstName: String) {
}
如果没一个有任何入口参数,Kotlin会初始化一个默认的构造函数。
而如果我们要在构造函数中写初始化代码,我们可以将其放在init代码块中,在自定义控件中一般做一些初始化熟悉,自定义属性,和画笔初始化操作。
init{
//初始化代码
}
注意:主构造函数的属性,可以在类体的属性初始化器中使用
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
次构造函数
类中也可以有重载式的构造函数,类之间的构造函数委托需要用this关键字委托给主构造函数,如果类没有主构造函数,需要用super委托给基类构造函数。
//this(name)委托自己给主构造函数
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
}
}
构造函数的默认可见性是public,如果你希望构造函数是不可见的,则需要声明一个带有非默认的可见性的空的主构造函数
class DontCreateMe private constructor () {
}
构造函数的参数都可以带一个默认值,当构造函数所有的都有默认值,则编译器会生成一个额外的无参数构造函数
创建类的实例
要创建类的实例,我们只需要普通的调用构造函数既可,在Kotlin中是没有new关键字,我们去调用时只需要像java中省去new关键字,其他和java的一样创建,分号在Kotlin中可以不写。
val invoice = Invoice()
val customer = Customer("Joe Smith")
继承
在Kotlin中所有的类都有一个共同的超类Any,对于没有任何声明的超类,超类类型默认是Any,它和java.lang.Object不一样,除了equals()、hashCode()和toString()外没有任何成员。要声明超类时候只需要将其放在类头冒号后面
open class Base(p: Int)
class Derived(p: Int) : Base(p)
基类类型可以直接初始化主类的主构造函数,而open是可以让主类可以直接被继承,在Kotlin中每一个类默认都是final类型,即是不可被继承的,要想被基类继承就需要用open关键字声明。并且和java不同的是,Kotlin需要显示标明可以被覆盖的成员(属性和方法)。基类继承主类的方法需要用override标注,不加,编译器就会报错。
Kotlin需要用open显示标明可以被覆盖的成员方法和熟悉,并且用override标明覆盖后的成员,这一点和java是有些不同的。
//方法覆盖
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
//属性覆盖
open class Foo {
open val x: Int get { …… }
}
class Bar1 : Foo() {
override val x: Int = ……
}
class Bar2(override val count: Int) : Foo
抽象类
抽象类用abstract标注,不需要用open标注一个抽象类或者函数,可以用抽象成员覆盖一个非抽象的开放成员。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
属性声明
Kotlin中类属性的声明可以用var表示可变或者val表示只读。
Getters 和 Setters
声明一个属性的完整语法是
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
其中如果属性类型可以从初始器上下文中推断出来,是可以省略的。
一个只读属性的语法和一个可变的属性的语法有两方面的不同:1、只读属性的用 val开始代替var 2、只读属性不允许 setter。
幕后字段
Kotlin中类是不能有字段的。但是当我们定义自定义访问器时,有时有一个幕后字段是有必要的,所以我们可以通过field标识符访问这个字段
var counter = 0 // 此初始器值直接写入到幕后字段
set(value) {
if (value >= 0)
field = value
}
延迟初始化属性lateinit
在有些时候属性声明为非空类型必须在构造函数中初始化。然而这是不方便的,比如我们使用Andoid的Context,你不能在声明的时候就提供其一个非空初始器,但你依然想在使用时避免空检查,这时你可以使用lateinit 修饰符标注该熟悉。
接口
接口与Java类似,既可以包含抽象方法的声明,也包含实现。与抽象类不同的是,接口无法保存状态。它可以有 属性但必须声明为抽象或提供访问器实现。
使用关键字interface来定义接口
interface MyInterface {
val prop: Int //抽象的
val propertyWithImplementation: String
get() = "foo"
fun bar()
fun foo() {
// 可选的方法体
}
}
//实现
class Child : MyInterface {
override val prop: Int = 29
//方法的实现需要加override
override fun bar() {
// 方法体
}
}
注意:接口中的属性要么是抽象的,要么提供访问器实现,在接口中的属性不能有幕后字段,因此接口中的声明的访问器不能引用声明的属性。
覆盖冲突
由于可以实现多个接口,必然会出现冲突问题,即多个接口之间可能有相同方法,所以我们实现时,需要实现多个接口继承的所有方法。这一规则适用于单个实现也可以适用于继承多个实现的方法。
可见性修饰符
Kotlin有四个可见性修饰符:private, protected, internal和public,如果没有显示指定修饰符的话,默认是public。
函数,属性和类,对象和接口可以在顶层声明,即直接在包内。
包中声明的可见性
- 如果你不指定任何可见性修饰符,默认为 public,这意味着你的声明 将- 随处可见;
- 如果你声明为 private,它只会在声明它的文件内可见;
- 如果你声明为 internal,它会在相同模块内随处可见;
- protected 不适用于顶层声明。
// 文件名:example.kt
package foo
private fun foo() {} // 在 example.kt 内可见
public var bar: Int = 5 // 该属性随处可见
private set // setter 只在 example.kt 内可见
internal val baz = 6 // 相同模块内可见
类内部声明成员的可见性
- private 意味着只在这个类内部(包含其所有成员)可见;
- protected—— 和 private一样 + 在子类中可见。
- internal —— 能见到类声明的 本模块内 的任何客户端都可见其 internal 成员;
- public —— 能见到类声明的任何客户端都可见其 public 成员。
如果覆盖了一个可见性成员,但是没有指明其可见性,该成员还是和父类一样的可见性。
局部变量,函数和类不能有可见性修饰符。
注意:模块和Android中的module一样,一个模块是编译在一起的一套Kotlin文件。
总结
这是第一部分类和对象基础的总结.