类的组成
Kotlin中类的基本组成可写成以下的形式,Kotlin也是使用class关键字声明一个类的,我在此也同时加入了name和age两个字段。
class Student: Person() { var name = "" var age = 1 fun eat() { println("$name is $age") }}
Kotlin中类的实例化并不需要new关键字
var student = Student()
继承与构造函数
主构造函数设我们现在有一个学生类与人类,根据关系学生属于人,应该继承人,但是Kotlin与Java并不同,Kotlin中非抽象类不可实现,就像Java中加了final关键字的类一样,如果要使Student继承Person我们需要在class前使用open
关键字来告诉kotlin。
open class Person { var age = 0}
Java中继承使用extends关键字,但是在Kotlin中需要这样写,且Persion需要加上括号,下文讲到。
class Student: Person() { var name = "" }
Kotlin还涉及主构造函数和次构造函数的区别,主构造函数将会是你最常用的构造函数,每个类都默认有一个不带参数的构造函数,这点与Java类似,你也可以显式的指定参数,主构造函数的特点是没有函数体,直接写在类名后面即可,但你可能会想如果没有函数体我想在初始化的时候执行一些操作怎么办,这时候我们可以使用init结构体,类似于Java中的static结构体:
class Student(val name: String,val age: Int): Person() { init { println("$name is $age") }}
fun main() { var s = Student("a",1)}
这样我们就创建了一个Student对象
那么Person的括号到底是干什么用的?实际上那个括号主要是为了选择构造函数,在Java中子类在构造函数中需要调用父类的构造函数,这个规定在Kotlin也是一样的,只不过Kotlin是子类的主构造函数调用父类中的哪个构造函数,在继承的时候通过括号来指定,看个代码就懂了。
我们修改Person的主构造函数为以下内容:
open class Person(name: String, age: Int) {}
此时,继承Person的子类Student开始报错了:
解决办法就是我们再给Student增加参数:
class Student(val name: String, val age: Int, name2: String, age2: Int): Person(name2, age2) { init { println("$name is $age") }}
注意,我们在Student类的主构造函数中增加name2和age2这两个字段时,不能再将它们声明成 val,因为在主构造函数中声明成val或者var的参数将自动成为该类的字段,这就会导致和父 类中同名的name2和age2字段造成冲突。因此,这里的name2和age2参数前面我们不用加任何关键字,让它的作用域仅限定在主构造函数当中即可。
次构造函数任何一个类只能有一个主构造函数,但是可以有多个次构造函数。次构造函数也可 以用于实例化一个类,这一点和主构造函数没有什么不同,只不过它是有函数体的。
Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造 函数(包括间接调用),这部分也不难,举个例子:
class Student(val name: String, val age: Int, name2: String, age2: Int): Person(name2, age2) { init { println("$name is $age") } constructor(name: String, age: Int): this(name, age, "name2", 2) { } constructor(): this("XuanRan", 18) { }}
次构造函数使用constructor来声明,这里我们定义两个,从上往下数,第二个通过this调用第一个构造函数,第一个构造函数通过this调用主构造函数。
此时,这三个构造函数均能正常使用。
无主有次有时候我们会出现一种情况,无主构造函数有次构造函数,这种情况在Kotlin也是允许的,但是基本见不到,在这种情况下,我们可以省略括号的写法,并使用super关键字,这点就和Java类似了:
class Student: Person { constructor(name: String, age: Int): super(name, age) { }}
数据类
我们有时候在Java开发中需要经常重写equals、hashCode、toString方法,但是Kotlin提供了一个修饰符能够自动的帮我们完成,那就是数据类,使用data修饰。
data class Phone(val brand: String, val price: Double)
他的效果等于Java中的
public class Phone { private String brand; private double price; public Phone(String brand, double price) { this.brand = brand; this.price = price; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Phone phone = (Phone) o; return Double.compare(phone.price, price) == 0 && Objects.equals(brand, phone.brand); } @Override public int hashCode() { return Objects.hash(brand, price); } @Override public String toString() { return "Phone{" + "brand='" + brand + '\'' + ", price=" + price + '}'; }}
单例类
在Java中单例类,我们需要通过一个静态变量来存储类对象,并且在获取实例的时候进行判断是否已初始化,如果已经初始化直接返回,否则创建新对象后返回,这种一成不变的套路在Kotlin可以直接通过一个object修饰符来完成。
object Phone { fun hello() { println("Hello!") }}
fun main() { // 直接像调用静态方法那般调用即可 Phone.hello()}
密封类
在Kotlin中,假设接口类中没有方法,那么可以直接省略大括号。这里我们定义两个Result的实现类,Success与Error
interface Resultclass Success(val msg : String) : Resultclass Error(val error : Exception) : Result
假设我们有一个方法需要根据Result来获取返回的信息,但是由于when的语法规则中必须存在默认的else分支,我们不得不需要编写else的代码:
fun getResultMsg(result: Result) = when(result) { is Success -> result.msg is Error -> result.error.message else -> throw RuntimeException()}
并且最重要是,假设以后Result多了更多的实现,我们可能会忘记在方法中添加其他的实现造成抛出异常,给他而密封类就可以很好的解决这个问题,密封类的关键词是sealed class
,我们修改Result的代码:
sealed class Resultclass Success(val msg : String) : Result()class Error(val error : Exception) : Result()
由于密封类是可以被继承的,所以在继承时需要加一个括号,然后我们修改getResultMsg的方法时就发现else是可以忽略的:
fun getResultMsg(result: Result) = when(result) { is Success -> result.msg is Error -> result.error.message}
并且假设Result类增加了新的继承,when这里会报错提示我们必须要考虑到新的情况。
类型强转
在Java中,我们可以通过(转换类型)
来实现类型的强制转换,但是在Kotlin中,我们就需要使用as
关键字进行类型转换:
// 将student这个变量强转成Studyvar s : Study = student as Study
接口
与Java一致,Kotlin的接口也是使用interface修饰,与Java不同的是Kotlin支持默认实现,而Java自JDK1.8之后才支持默认实现。
interface Study { fun doHomeWork() fun doReadBook() { println("read book.") }}
接口的实现在Java中是implements,在Kotlin中则是冒号,然后多实现使用逗号隔开。
class Student(name: String, age: Int, val clazz: String): Person(name, age), Study { override fun doHomeWork() { println("do homework.") }}
我们在此节补充一下Java和Kotlin中可见性修饰符。
修饰符 | Java | Kotlin |
---|---|---|
public | 所有类可见 | 所有类可见(默认) |
private | 当前类可见 | 当前类可见 |
protected | 当前类、子类、同一包路径下的类可见 | 当前类、子类可见 |
default | 同一包路径下的类可见(默认) | 无 |
internal | 无 | 同一模块中的类可见 |