Kotlin 中使用关键字 class 声明类
class Invoice { /*……*/ }
1.构造函数
在 Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。主构造函数是类头的一部分:它跟在类名(与可选的类型参数)后。
class Person constructor(firstName: String) { /*……*/ }
如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。
class Person(firstName: String) { /*……*/ }
2.次构造函数
请注意,初始化块中的代码实际上会成为主构造函数的一部分。委托给主构造函数会作为次构造函数的第一条语句,因此所有初始化块与属性初始化器中的代码都会在次构造函数体之前执行。即使该类没有主构造函数,这种委托仍会隐式发生,并且仍会执行初始化块:
class Constructors(name:String) { //主构造函数
init { //初始化
println("Init block")
}
constructor(i: Int) { //次构造函数
println("Constructor")
}
constructor(i: Int,a:String) { //次构造函数
println("Constructor")
}
}
3.创建类的实例
要创建一个类的实例,我们就像普通函数一样调用构造函数:
val invoice = Invoice()
val customer = Customer("Joe Smith")
注意 Kotlin 并没有 new 关键字。
4.继承
在 Kotlin 中所有类都有一个共同的超类 Any
,这对于没有超类型声明的类是默认超类
class Example // 从 Any 隐式继承
如需声明一个显式的超类型,请在类头中把超类型放到冒号之后:
open class Base(p: Int) //显式的超类型
class Derived(p: Int) : Base(p)
如果派生类有一个主构造函数,其基类可以(并且必须) 用派生类主构造函数的参数就地初始化。
如果派生类没有主构造函数,那么每个次构造函数必须使用 super 关键字初始化其基类型,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
5.覆盖方法
我们之前提到过,Kotlin 力求清晰显式。因此,Kotlin 对于可覆盖的成员(我们称之为开放open)以及覆盖后的成员需要显式修饰符:
Base.v()
函数上必须加上 override 修饰符。如果没写,编译器将会报错。 如果函数没有标注 open 如 Base.nv()
,那么子类中不允许定义相同签名的函数, 不论加不加 override。将 open 修饰符添加到 final 类(即没有 open 的类(Derived)或方法(Base.nv()))的成员上不起作用。
标记为 override 的成员本身是开放的,也就是说,它可以在子类中覆盖。如果你想禁止再次覆盖,使用 final 关键字:
6.覆盖属性
属性覆盖与方法覆盖类似;在超类中声明然后在派生类中重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。 每个声明的属性可以由具有初始化器的属性或者具有 get
方法的属性覆盖。
open class Shape {
open val vertexCount: Int = 0
}
class Rectangle : Shape() {
override var vertexCount = 4
}
你也可以用一个 var
属性覆盖一个 val
属性,但反之则不行。 这是允许的,因为一个 val
属性本质上声明了一个 get
方法, 而将其覆盖为 var
只是在子类中额外声明一个 set
方法.
请注意,你可以在主构造函数中使用 override 关键字作为属性声明的一部分。
interface Shape {
val vertexCount: Int
}
class Rectangle(override val vertexCount: Int = 4) : Shape // 主构造函数中使用 override 关键字作为属性声明的一部分
class Polygon : Shape {
override var vertexCount: Int = 0 // 以后可以设置为任何数
}
7.调用超类实现
派生类中的代码可以使用 super 关键字调用其超类的函数与属性访问器的实现: super. 调用父类方法或属性;super@ 内部类中访问外部类的超类 ; super<父类> 继承多个超类 有相同的方法必须覆盖这个成员提供自己的实现。
open class Foo { //超类
open fun f() { println("Foo.f()") }
open fun run(){ println("Foo.f()")}
open val x: Int get() = 1
}
interface Doo{ //接口
open fun run() { println("Doo.f()") }
}
class Bar : Foo(),Doo{
override val x: Int get() = 0
override fun f() {
println("Bar.f()")
super.f(); //调用父类方法或属性
}
override fun run() { //一个类从它的直接超类继承相同成员的多个实现, 它必须覆盖这个成员并提供其自己的实现(也许用继承来的其中之一)来消除歧义
super.run()
super.run()
}
inner class Baz { //内部类
public fun g() {
[email protected]() // 调用 Foo 实现的 f()
println([email protected]) // 使用 Foo 实现的 x 的 getter
}
}
public fun dosthing() { //使用于调用内部类方法。
Baz().g();
}
}
8.抽象类
类以及其中的某些成员可以声明为 abstract。 抽象成员在本类中可以不用实现。 需要注意的是,我们并不需要用 open
标注一个抽象类或者函数——因为这不言而喻
我们可以用一个抽象成员覆盖一个非抽象的开放成员 。注意:
抽象类不能用于创建实例,只能当作父类被其子类继承。
abstract不能用于修饰局部变量,Kotlin中没有抽象变量的说法;
使用abstract关键字修饰的方法必须被其子类重写才有意义
open class Polygon {
open fun draw() {}
}
abstract class Rectangle : Polygon() {
abstract override fun draw()
val a="1"
abstract val type: String
}
class person:Rectangle(){
override fun draw() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override val type: String
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
}
Kotlin 类中的属性既可以用关键字 var 声明为可变的,也可以用关键字 val 声明为只读的。
class Address {
var name: String = "Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}
如果只读属性的值在编译期是已知的,那么可以使用 const 修饰符将其标记为编译期常量。 这种属性需要满足以下要求:
String
或原生类型值初始化这些属性可以用在注解中:
// companion object 修饰为伴生对象,伴生对象在类中只能存在一个,类似于java中的静态方法 Java 中使用类访问静态成员,静态方法。
companion object {
const val C: Int = 0 //常量
/**main入口函数 **/
@JvmStatic
fun main(args: Array) {
}
}
Kotlin 的接口可以既包含抽象方法的声明也包含实现。与抽象类不同的是,接口无法保存状态。它可以有属性但必须声明为抽象或提供访问器实现。接口成员默认就是“open”的
interface MyInterface {
val prop: Int // 抽象的
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(prop)
}
}
class Child : MyInterface {
override val prop: Int = 29
}
实现多个接口时,可能会遇到同一方法继承多个实现的问题。例如
interface A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super.foo()
super.foo()
}
override fun bar() {
super.bar()
}
}
上例中,接口 A 和 B 都定义了方法 foo() 和 bar()。 两者都实现了 foo(), 但是只有 B 实现了 bar() (bar() 在 A 中没有标记为抽象, 因为没有方法体时默认为抽象)。因为 C 是一个实现了 A 的具体类,所以必须要重写 bar() 并实现这个抽象方法。
然而,如果我们从 A 和 B 派生 D,我们需要实现我们从多个接口继承的所有方法,并指明 D 应该如何实现它们。这一规则既适用于继承单个实现(bar())的方法也适用于继承多个实现(foo())的方法。
类的修饰符包括 classModifier 和_accessModifier_:
classModifier: 类属性修饰符,标示类本身特性。
abstract // 抽象类
final // 类不可继承,默认属性
enum // 枚举类
open // 类可继承,类默认是final的
annotation // 注解类
accessModifier: 访问权限修饰符
private // 仅在同一个文件中可见
protected // 同一个文件中或子类可见
public // 所有调用的地方都可见
internal // 同一个模块中可见