类与对象
1.类
1.1定义类
可以使用如下代码定义一个类,以及声明它所拥有的字段和函数:
class Person {
var name = ""
var age = 0
fun eat() {
println(name + " is eating. He is " + age + " years old.")
}
}
然后使用如下代码创建对象,并对对象进行操作:
fun main() {
val p = Person()
p.name = "Jack"
p.age = 19
p.eat()
}
和java实例化方式类似,只是去掉类new关键字,之所以这么设计,是因为当你调用了某个类的构造函数时,你的意图只可能时对这个类的实例化。
1.2 继承
Kotlin中一个类默认是不可以被继承的,如果想要让一个类可以被继承,需要主动声明open关键字:
open class Person {
var name = ""
var age = 0
fun eat() {
println(name + " is eating. He is " + age + " years old.")
}
}
要让另一个类去继承Person类,则需要使用冒号关键字:
class Student : Person() {
var sno = ""
var grade = 0
}
为什么Person类后面要加上一对括号,详见:
Kotlin的面向对象编程,深入讨论继承写法的问题
2.接口
Java是但继承结构的语言,任何一个类最多智能继承一个父类,但可以实现任意多个接口,kotlin也是如此,Kotlin中定义接口的关键字和Java中是相同的,都是使用的interface:
interface Study {
fun readBooks()
fun doHomework()
}
而Kotlin中实现接口的关键字变量了冒号,和继承使用的是同样的关键字:
class Student(val name: String, val age: Int) : Person(name, age), Study {
override fun readBooks() {
println(name + " is reading.")
}
override fun doHomework() {
println(name + " is doing homework.")
}
}
和Java不同的是,Kotlin 的接口中定义的函数可以进行默认实现(Java 1.8之后也支持这个功能)
interface Study {
fun readBooks()
fun doHomework(){
println("do homework default implementation.")
}
}
3.修饰符
- private:对当前类内部可见
- public:与Java一致,表示对所有类可见,但在Kotlin中,public修饰符是默认项。而在Java中,default是默认项。
- protected:Java中表示对当前类,子类和同一包路径下的类可见,在Kotlin中表示只对当前类和子类可见。
- internal:Kotlin抛弃了Java中的default(同一包路径下的类可见),引入了internal,只对同一模块中的类可见。
修饰符 | Java | Kotlin |
---|---|---|
public | 所有类可见 | 所有类可见(默认) |
private | 当前类可见 | 当前类可见 |
protected | 当前类,子类,同一包路径下类可见 | 当前类,子类可见 |
default | 同一包路径下类可见(默认) | 无 |
internal | 无 | 同一模块中的类可见 |
4.数据类与单例类
4.1 数据类data
Kotlin中使用data关键字可以定义一个数据类:
data class CellPhone(val brand:String, val price:Double) {
// override fun equals(other: Any?): Boolean {
// return super.equals(other)
// }
}
Kotlin会根据数据类的主构造函数中的参数将equals()、hashCode()、toString()等固定且无实际逻辑意义的方法自动生成,从而大大简少了开发的工作量。
fun main() {
val c1 = CellPhone("sam", 99.0)
val c2 = CellPhone("sam", 99.0)
println(c1)
println("equals " + (c1 == c2))
}
//CellPhone(brand=sam, price=99.0)
//equals true
4.2 单例类object
在Java中实现单例类并不复杂: Java设计模式——单例模式.但在Kotlin中则更加简单,只需要将class关键字改为object关键字即可:
object Singleton {
fun single(){
println("------signle-----")
}
}
而调用单例类中的函数比较类似于Java中静态方法的调用方式:
fun main() {
Singleton.single()
}
//在java类中调用kotlin单例类
class JavaClass {
public void javaTest(){
Singleton.INSTANCE.single();
}
}
这种写法虽然看上去像是静态方法的调用,但其实Kotlin在背后自动帮我们创建了一个Singleton类的实例,并且保证全局只会存在一个Singleton实例。
4.3 密封类sealed
有时候我们会有这样的使用场景,先看代码:
interface Result {}
class Success(val msg:String):Result
class Failed(val error:Exception):Result
fun getResultMsg(result: Result) = when(result){
is Success -> result.msg
is Failed -> result.error.message
else -> println("else")
}
此代码中,如果getResultMsg方法中我们不写else分支,编译器会认为我们when语句中缺少条件分支,会提示错误,但else语句却永远执行不到,但是当我们有另外一个类实现了Result接口时,如:
class WaitWait(val msg:String):Result
class Unknown(val msg:String):Result
如果getResultMsg方法中接收到的result实际类型为WaitWait或者Unknown时,最终只会执行else语句了,这样的代码既不健壮,又不合理。如果想要解决这个问题,就需要使用密封类了。密封类的关键字是sealed,使用也很简单,我们可以将Result接口修改为密封类的写法:
sealed class Result {}
class Success(val msg:String):Result()
class Failed(val error:Exception):Result()
class WaitWait(val msg:String):Result()
class Unknown(val msg:String):Result()
fun getResultMsg(result: Result) = when(result){
is Success -> result.msg
is WaitWait -> result.msg
is Unknown -> result.msg
is Failed -> result.error.message
}
这样在when语句中,我们也就不再需要使用else语句了,并且当when语句中传入一个密封类变量作为条件时,Kotlin编译器会自动检查该密封类有哪些子类,并强制要求你将全部子类都作为when语句的分支条件进行处理,否则会编译报错。这里需要注意的是:密封类及其所有子类只能定义在同一个文件的顶层位置,不能嵌套在其它类中。