Kotlin的类型系统
可空类型 需要显式的指出那些是可以为空的类型
fun strLength(string: String) = string.length
//调用
strLenght(null)//这样调用,IDE会报错,因为函数声明的是不可空类型
如果可可以传入null类型的需要在类型后面加?应该这样
fun strLength(string: String?) = string.length//报错
如果是可空类型不能直接调用调用其方法,需要经过if判断,或者(?.)判断又或者!!断言比如上面的例子应该这样调用
fun strLength(string: String?) = string?.length//如果为空则会返回null
类型的定义
什么是类型?
为什么变量拥有类型?
类型就是数据的分类,决定了该类型可能拥有的值,以及能调用的方法
安全调用运算符 ?.
foo?.bar() //等价于
if(foo!=null) bar() else null
图解
Elvis运算符 ?:
当你值为空时候不想返回null值使用
val f = foo?:bar
class Address(val streetAddress: String,val zipCode:String,val city:String)
class Company(val name: String,val address: Address?)
class Person(val name: String,val company: Company?)
fun printShoppingLabel(person: Person){
person.company?.address?:throw IllegalArgumentException("No city")
}
安全转换云算符 as?
class Person(private val firstName: String, private val lastName: String) {
override fun equals(other: Any?): Boolean {
val person = other as? Person ?: return false //直接返回
return person.firstName == firstName && person.lastName == lastName
}
override fun hashCode(): Int {
return firstName.hashCode() * 64 + lastName.hashCode()
}
}
非空断言 !!
let 函数
处理可空表达式更容易 当我们需要把一个可空类型传递给一个非空类型
fun sendEmail(name: String) = {}
val email: String?
sendEmail(email)//报错
正确的做法
val email: String? = "Kotlin@yy.com"
email?.let { sendEmail(it) }
延时初始化属性
比如:在JUnit中
class MyTest {
private var myService: MyService? = null //这里看起来很多余
@Before
fun setUp() {
this.myService = MyService() //JUnit 要求需要把初始化的属性放到 @Before注解里面
}
fun testAction() {
myService!!.performClick() //不需要检测null直接访问属性
}
}
这里可以用lateinit 延迟初始化
private var myService: MyService
可空类型的拓展
比如String?函数 可以直接调用 isEmpty() 或者 isBlank()方法判断是否为空类型
这里使用的就是拓展函数
自己也可以定义一个拓展函数,isEmptyOrNull() 和isNullOrBlank()
fun String?.isEmptyOrNull(): Boolean = this == null || this.isEmpty()
//调用
var lateinit email:String?
if(email.isEmptyOrNull){
/**do something**/
}
类型参数的可空性
kotlin中,所有泛型参数或者泛型类型都是可空类型
fun <T> printHandler(t: T) {
print(t.hashCode())
}
//如果是1.3以下标准库这里会报错,
//1.3kotlin标准库已经对Any类进行了拓展,不会报错
//如下
@SinceKotlin("1.3")
@InlineOnly
public inline fun Any?.hashCode(): Int = this?.hashCode() ?: 0
可空性和Java识别
Java是不支持可空类型的,这里主要通过Java中的注解识别可空的类型 @Nullable String 会被Kotlin识别为String? @NotNull String => String
| Java | Kotlin |
|---|---|
@Nullable String |
String? |
@NotNull String |
String |
在Kotlin处理Java类型,可以是可空也可以处理成非空类型
interface StringProcress{
void procress(String value)
}
使用Kotlin重新上面Java代码
class StringPrinter: StringProcress{
@Override procress(value: String){
println(value)
}
}
class StringPrinter: StringProcress{
@Override procress(value: String?){
if(value!=null){
println(value)
}
}
}
用Kotlin调用Java方法,需要自己判断类型是否为可空类型
基本数据类型和其他类型
Kotlin并不区分基本类型和他们的包装类型
- 基本数据类型:Int、Boolean及其他
Kotlin和Java对应的数据类型
| 类型 | Kotlin | Java |
|---|---|---|
| 整数类型 | Byte、Short、Int、Long | |
| 浮点数据类型 | Float、Double | |
| 字符类型 | Char | |
| 布尔类型 | Boolean |
可空的基本数据类型
Kotlin中可空类型不能使用Java的基本数据类型表示,因为null只能被存储到Java的引用类型变量中。这意味着任何时候使用了基本数据类型的可空版,就会编译成对应的包装类。
数字转换
- Kotlin不会自动把一种数据类型转换成另外一种。如果是转换成范围更大的类型需要手动转换,比如:
val i = 1
val i: Long = 1 //报错
vali = 1
val i: Long = i.toLong() //另外听过了toByte() toInt() toChar()等
基本数据类型书面值
| 类型 | 类型 | 字面值 | 例子 |
|---|---|---|---|
| Long | 后缀L | 123L | |
| 浮点数Double | 字面值 | 0.12、2.0 | |
| Float | 后缀F | 123.4F 123.4f | |
| 16进制 | 前缀0x | 0xCAFEBABE | |
| 2进制 | 前缀0b | 0x000000101 |
Any Any?根类型
和Object作为Java类层级结构差不多
把基本类型赋值给Any会自动装箱,跟Java一样。
val num: Any = 3 //这里会自动装箱
Unit类型:Kotlin的“void”
fun f():Unit{/**空返回值**/}
Unit类型和Java中“void”的区别:Unit可以作为参数类型,void不能作为参数类型
集合和数组
- 可空值得集合 遍历使用filterNotNull
list.filterNotNull()
- 只读集合与可变集合
Kotlin集合把访问集合数据接口和修改集合数据接口分开。
Collection 只读
MutableCollection 可写
如下图显示
只读接口不一定是不可变的 比如:
- Kotlin集合和Java
Kotlin中集合的层级关系:
创建集合:
| 集合类型 | 只读 | 可变 |
|---|---|---|
| List | listOf | mutableListOf、arrayListOf |
| Set | setOf | mutibleSetOf、hashSetOf、linkedSetOf、soredSetOf |
| Map | mapOf | mutibleMapOf、hashMapOf、linkedMapOf、soredMapOf |
- 作为平台类型的集合(Kotlin把定义在Java代码中的类型看出平台类型) 在Kotlin中需要考虑
- 集合是否可空?
- 集合中元素是否可空?
- 你的方法会不会修改集合?
- 对象和基本数据类型的数组
End