类和继承
类
class Invoice{
}
类的声明包括 class关键字 类名 类头(第一构造函数) 类体。并且类头和类体是可以省略的
class Empty
构造函数
在kotlin中,一个类有一个“第一”构造函数和多个“第二”构造函数。并且第一构造函数是类头的一部分,被放置在类名的后面。
class Persion constructor(firstName : String){
}
如果第一个构造函数没有注解或者其他可见标识,则可以将关键字constructor省略掉
class Persion(firstName : String){
}
第一构造函数不包含任何代码。初始化的代码放在init{} 方法体中。
class InitOrderDemo(name : String){
val firstProperty = "the first Property : $name".also(::println)
init{
println("the first initializer block that prints ${name}")
}
val secondProperty = "the second Property : ${name.length}".also(::println)
init{
println("the second initializer block that prints ${name.leght}")
}
}
另外需要注意的是,第一构造函数的参数不紧能够在初始化块(init)中使用,还可以在成员变量初始化的时候使用。
class Customer(name : String){
val customerKey = name.toUpperCase()
}
实际上,为了声明成员属性并且在第一构造函数中初始化它们,kotlin提供以下简介的格式:
class Person(val firstName : String , val lastName : String , var age : Int ){
}
可变的使用var , 只读的使用 val
如果第一构造函数,有修饰符,则关键字constructor不能够省略
class Customer public @Inject constructor(name : String){
}
第二构造函数
class Person{
costructoer(parent : Person){
parent.children.add(this)
}
}
如果一个类有第一构造函数,每一个第二钩构造构造函数需要委托到第一构造函数(无论是直接,还是简介)
class Person(name : String){
constructor(name :String , parent : Person):this(name){
parent.children.add(this)
}
}
注意init 块中方法的执行在第二构造函数方法体执行之前执行。即使该类没有显示标明第一构造函数。而是通过隐式代理。
class Constructors{
init{
println("initializer block")
}
constuctor(i : Int){
println(" a second constructor")
}
}
执行结果: initializer block
a second constructor
默认情况一下,一个非抽象类,有一个公有无参构造函数,如果不想用户创建该类实例,可将构造函数私有化
class God private constructor(){
//这样就能够造神了
}
注意:在JVM中,第一构造函数的所有参数都有默认值,编译器将会生成一个,使用这些默认值的无参构造构造函数。
class Customer(val customerName : String = "")
创建类的实例对象
为了创建类的实例,我们需要调用构造函数
val invoice = Invoice()
val customer = Customer("Lebron James")
注意: kotlin中没有 new 关键字
创建一个嵌套实例,内部类和匿名内部类被声明在嵌套类
类成员
- 构造函数和初始化器
- 函数
- 属性
- 嵌套和内部类
- 对象声明
继承
在Kotlin的世界中Any是任何类的父类。
class Example //隐式继承Any
注意:Any并不是Java中的Object类,它只是提供了equals()、hashCode() 和toString()方法。
为了显示继承父类,我们在把父类放在冒号之后。
open class Base(p:Int)
class Derived(p:Int) :Base(p)
这里的open描述符在类中含义与java中的final相反。它允许该类被继承。kotlin 中的是不能够被继承的。
如果子类有一个第一方法,父类必须在那里使用第一构造方法中的参数初始化。
如果一个没没有第一构造函数,每一个第二构造函数必须使用super关键字类初始化父类。
class MyView :View{
constructor(ctx:Context):super(ctx)
constructor(ctx:Context,attrs:AttributeSet):super(ctx,attrs)
}
重写方法
正如我们之前提到的,我们坚持显示的说明某些事情。不像java,kotlin需要表明哪些方法可重写
open class Base{
open fun v(){}
fun nv(){}
}
class Derived():Base(){
override fun v(){}
}
在子类进行重写时,关键字override是必不可少的,否则编译器会报错。如果在父类中的方法没有关键字open,在子类中重写该方法,编译器同样会报错,要么使用重写,要么移除掉他。通常在一个final类中 open 某个方法是被禁止的。一言以蔽之,vorride 和 open是成对的。
open class AnotherDerived():Base(){
final override fun v(){} //禁止该类的子类重写该方法
}
重写属性
重写属性和重写方法是类似的。在父类中声明的属性,如果在类型再次被声明,需要添加关键字override,并且类型是兼容的。每一个声明的属性能够被重写,以getter、setter方法和初始化器。
open class Foo{
open val x: Int get(){}
}
class Bar1:Foo(){
override val x:Int =
}
你可以使用一个var属性重写val属性,但是反过来,不成立。这是因为一个val属性本质上声明了一个getter方法,并且重写var在子类另外还生成一个setter方法。
interface Foo{
val count: Int
}
class Bar1(ovrride val count :Int):Foo
class Bar2:Foo{
override val count:Int = 0
}
子类的初始化顺序
在一个子类实例的构建过程中,首先被初始化的是基类,在子类初始化之前。
open class Base(val name:String){
init{
println("Init Base")
}
open val size:Int = name.lenght.also{
println("Init size int Base : $it")
}
}
class Derived(name :String , val lastName:String):Base(name.capitalize().also{
println("Argument for Base : $it")
}){
init{
println("InitiaLizing Derived")
}
override val size:Int = (super.size + lastName.length).also{
println("InitSize in Derived : $it");
}
}
那也就意味着,截止到父类构造函数被执行时,子类中被声明或者重写的属性尚未初始化。如果有一些属性在父类的初始化逻辑中被使用,可能会出现问题。因此,在设计一个基类时,避免在狗仔函数、属性初始化、init块中使用open成员。
调用父类的实现
子类通过使用super关键实现调用父类函数和属性。
open class Foo{
open fun f(){
println("Foo.f()")
}
open val x:Int get() = 1
}
class Bar : Foo{
override fun f(){
super.f()
println("Bar.f()")
}
overide val x:Int get() = super.x + 1
}
在内部类中访问外部类的父类使用super关键字加上外部类的类名
class Bar:Foo{
override fun f(){
}
override val x:Int get() = 0
inner class Baz{
fun g{
super@Bar.f()
//[email protected]() 外部类的f()被调用
println(super@Bar.x)
}
}
}
重写的规则
在kotlin中,继承重写的规则如下:
如果一个类多继承了其他类,而在该类的父类中有多个同一签名的方法,则必须在该类中实现重写。可以通过super的形式调用某个父类的方法
open class A{
open fun f(){
print("A")
}
fun a(){
print("a")
}
}
interface B {
fun f(){
print("B")
}
fun b(){
print("b")
}
}
class C():A(),B{
override fun f(){
super<A>.f()
super<B>.f()
}
}
抽象类
一个类和类中的成员有可能被声明为抽象的。一个抽象成员在该类中是没有实现的。我们不必注明一个抽象类和成员是 open的。
我们可以用一个抽象成员继承自费抽象成员。
open class Base{
open fun f(){}
}
abstract class Derived:Base(){
override abstract fun f()
}
Companion Objects 静态
在Kotlin中,不像Java、C#,类中没有静态方法。在大多数情况下,推荐使用包级别的函数替代。
如果你需要写一个函数,该函数的调用不需要类实例,但是能够方位类的内部,你可以在类的内部声明一个 “Object Declaration”的成员。
如果你在类中声明一个 companion object,你将可以像java中调用静态成员一样,调用它。