本章节主要介绍Kotlin的类型定义和简单使用
类的定义
Kotlin类默认为public,类内无内容可省略
Kotlin类成员变量,方法同Java类似
Kotlin类默认带无参构造器,如果需要定义其他构造器,使用constructor
关键字创建
也可以直接定义到类上
类的实例化
直观感受是,省略了
new
关键字,获得对象再也不需要new
了
Java
SimpleClass simpleClass = new SimpleClass(9);
System.out.println(simpleClass.x);
simpleClass.y();
Kotlin
val simpleClass = SimpleClass(9)
println(simpleClass.x)
simpleClass.y()
接口的定义
基本和Java一致
接口的实现
implements
关键字换成了:
@override
注解换成了override
关键字
抽象类的定义
由于Kotlin的类默认是final,所以需要添加open关键字,使之可以被继承
类的继承
extends
关键字换成了:
,跟接口实现保持了一致- 需要调用被继承方的构造器(默认为无参构造器)
类的属性(成员变量)
- var:默认带
getter
和setter
- val:默认带
getter
也可以自己定义getter
和setter
方法
class Person(age: Int, name: String) {
var age: Int = age
get() {
return field
}
set(value) {
field = value
}
var name: String = name
}
属性引用
fun main() {
val ageRef = Person::age // 未绑定 Receiver
val person = Person(18, "Bennyhuo")
val nameRef = person::name // 绑定 Receiver
// 属性引用
ageRef.set(person, 20)
nameRef.set("Andyhuo")
}
接口属性
接口可以定义属性,但是不能赋值
interface Guy {
var moneyLeft: Double
get() {
return 0.0
}
set(value) {
}
fun noMoney() {
println("no money called.")
}
}
接口属性没有backing field
我们尝试跟上面的类一样定义getter
和setter
方法
interface Guy {
var moneyLeft: Double
get() {
return field
}
set(value) {
field = value
}
fun noMoney() {
println("no money called.")
}
}
将会获得编译器提示"Property in an interface cannot have a backing field"
扩展方法
这是Kotlin的大杀器,非常好用,一些工具类完全可以用扩展方法来替代,并且使用起来非常方便
可以为现存的类定义新的方法
operator fun String.minus(right: Any?) = this.replaceFirst(right.toString(), "")
operator fun String.times(right: Int): String {
return (1..right).joinToString("") { this }
}
operator fun String.div(right: Any?): Int {
val right = right.toString()
return this.windowed(right.length, 1, transform = {
it == right
}) // [false, false, false, false ... false, true, ..., true]
.count { it }
}
fun main() {
val value = "HelloWorld World"
println(value - "World")
println(value * 2)
val star = "*"
println("*" * 20)
println(value / 3)
println(value / "l")
println(value / "ld")
}
空指针安全特性
空类型安全
注意:String和String?不是一个类型
var nonNull: String = "Hello"
nonNull = null // 编译器报错
var nullable: String? = "Hello"
nonNull = null // 编译通过
强转为不可空类型
var nullable: String? = "Hello"
val length = nullable!!.length
安全访问
var nullable: String? = "Hello"
nullable = null
val length = nullable?.length
?.
的语法结构,我最早是在TypeScript里面看到的,基本逻辑就是,如果为空,则不会继续往下执行,一定程度上杜绝了NPE异常,而现在Kotlin把它引入了,非常棒
elvis运算符(?:)
var nullable: String? = "Hello"
nullable = null
val length: Int = nullable?.length ?: 0
nullable == null
时,length = 0nullable != null
时,length = nullable!!.length
平台类型
Kotlin对于Java类库,是百分百支持的,但是Java里面没有String?
这种类型,Kotlin怎么处理呢?是当成非空还是可空类型?
答案是:Kotlin编译器不判断,有用户自己来判断是否可空还是非空
比如在Java里面定义了一个Person
public class Person {
public String getTitle() {
// ...
}
}
那么在Kotlin里面调用时,person.title
的类型是String!
,这个String!
就是平台类型,可以非空也可以可空
val person = Person()
val title: String! = person.title
智能类型转换
自动转换为子类型
val kotliner: Kotliner = Person("benny", 20) // Person是Kotliner的子类
if(kotliner is Person) {
println(kotliner.name) // 自动转换类型为 Person,不用向Java一样进行强转
}
可空转非空
var value: String? = null
value = "benny"
if(value != null) {
println(value.length) // 可空转非空,所以我们也可以说,非空类型是对应的可空类型的子类
}
不支持智能类型转换的场景
在线程不安全的调用下,智能类型转换失效
var tag: String? = null
fun main() {
if(tag != null) {
println(tag.length) // 虽然判断不为空,但是其他线程可能对它进行修改
}
}
类型的安全转换(as?)
val kotliner: Kotliner = ...
println(kotliner as? Person).name) // 安全转换,失败返回null
针对类型的智能转换,有几个建议
- 尽可能使用val来声明不可变引用,让程序的含义更加清晰确定
- 尽可能减少函数对外部变量的访问,也为函数式编程提供基础(函数式编程的精髓就是拒绝副作用)
- 必要时创建局部变量指向外部变量,避免因它变化引起程序错误