类声明
和Java一样,Kotlin中使用关键字class来声明一个类。如下即是声明一个最简单的没有任何属性和方法的类
// 没有任何属性、方法的Invoice 类
class Invoice {}
一个完整的类声明包含类名,类头(指定构造参数、构造方法等),类体(用大括号包裹的部分)。类头和类体这两个部分并非必要的,类头和类体都是可选的; 如果一个类没有类体,可以省略花括号。
class Invoice
构造函数
在 Kotlin 中的一个类可以有一个主构造函数和一个或多个次构造函数。主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后。
class Person constructor(firstName: String) {
}
如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。
class Person(firstName: String) {
}
主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中:
class Customer(p : Int) {
init {
println("Customer类初始化")
}
}
次构造函数
二级构造函数,也称为次级构造函数。关于二级构造函数,主要有以下几点:
次级构造函数不能省略constructor关键字;
当类拥有主构造函数时,任何一个二级构造函数都需要直接或间接通过另一个二级构造函数代理主构造函数;
类中的一个构造函数代理另一个构造函数,需要使用关键字this;
类也可以声明前缀有 constructor的次构造函数:
class Person {
constructor(parent: Person) {
parent.children.add(this )
}
}
class Person constructor(id: Int) {//(构造函数No.0)主构造函数
var id = id//主构造函数初始化id
var name = ""
var age = 0
//(构造函数No.1)直接代理主构造函数
constructor(name: String, id: Int) : this(id) {
this.name = name
}
//(构造函数No.2)代理了构造函数No.1,间接代理主构造函数
constructor(name: String, age: Int, id: Int) : this(name, id) {
this.age = age
}
}
继承
和所有的Java类都有一个共同的父类Object一样且不支持同时继承多个父类。Kotlin中所有的类都拥有一个共同的父类Any(但Any不是Object,不要搞错)。Any相比Object其内部结构要简单很多,仅有equals()、hashCode()、toString()三个抽象方法。
//默认情况下,在 Kotlin 中所有的类都是 final,
//抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的。抽象成员在类中不存在具体的实现。
open class Base(p: Int){
init {
println("基类")
}
//重写
//在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。
// 如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词:
open fun sdutent() {
println("基类方法")
}
}
class Customer(p : Int): Base(p) {
init {
println("Customer类")
}
constructor(p: Int,n: String):this(p){
println("$p is n= $n")
}
override fun sdutent() {
super.sdutent()
println("重写基类的方法")
}
}
属性和赋值
在声明一个最简单的空壳类之后,我们来为它增加一些类属性。Kotlin中类的属性可以用var或者val关键字进行声明,其中var为可变属性,val为只读属性(相当于Java的final)。
class Student {
var name = "名字" //名字属性可变,用var
val birthday = "1994-10-26" //生日属性不可变,用val
}
像上面这样就简单的为Student类声明了name和birthday两个属性,且在声明属性时进行了初始化,按照Kotlin的类型推断特点,name和birthday就是属于String类型(不知道类型推断的同学可以翻阅前面的写的文章)。现在我想为Student类添加一个age属性,但是我并不想在声明时进行初始化,用Java写起来非常简单即可实现
public class JavaStudent {
private String name = "名字";
private String birthday = "1994-10-26";
private int age;//Java版的实现
}
按照Java的实现套路直接套入Kotlin你会发现IDE直接报错并提示property must be initialized or be abstract。
按照提示我们必须把类和字段都声明为abstract才可以通过编译。
abstract class Student {
var name = "名字" //名字属性可变,用var
val birthday = "1994-10-26" //生日属性不可变,用val
abstract var age: Int
}
这样未免太过麻烦,而且理解起来也非常奇怪。Kotlin提供了延迟初始化的方式来解决初始化的问题,使用关键字lateinit即可,这样就无需声明abstract了。
可惜使用lateinit延迟初始化age之后,IDE依旧报错,这次提示的内容是lateinit modifier is not allowed on primitive type properties。Kotlin并不支持对原生类型进行lateinit,为什么呢?因为Kotlin会使用null来对每一个用lateinit修饰的属性做初始化,而基础类型是没有null类型,所以无法使用lateinit。这点很好理解,就像可以把int型变量赋值为0,却无法赋值为null一样。所以这里为了通过IDE的编译,我们可以采用两种方案,要么还是直接在age声明时进行初始化,要么不用基础类型来定义age。
class Student {
var name = "名字" //名字属性可变,用var
val birthday = "1994-10-26" //生日属性不可变,用val
var age = 0 //直接使用0初始化age,age为Int型
lateinit var ageStr: String //String不是基础类型,可以使用lateinit初始化
}
创建对象
调用类的构造器,调用方式和使用普通函数一样:
val person = Student ()