本文共 6406 字,大约阅读时间需要 21 分钟。
问好可以加在任何类型的后面来表示这个类型的变量可以存储null引用:String?, Int?, 等等

在Kotlin中没有问好类型表示的这种类型的变量不能存储null引用。说明Kotlin中的所有常见类型都是非空给。
比如下面的调用会被编译器报错
fun strLen(s : String) = s.lengthfun main(args : Array){ strLen(null) //这里编译器会报错} 
注意:一旦你有一个可空类型的值,能对他进行的操作也会受到限制。



使用姿势:
如果你在函数里增加了null的检查,即就是和null进行了比较,此时编译器就会记住,并且在这个比较发生的
作用域内把这个值当作非空处理。
demo:
fun strLen(s : String?) : Int{       return if(s != null)        s.length    else        0}strLen(null)   //合法的代码   解释:允许把一次null检查和一次方法调用合并为一个操作。
fun printAllCaps(s : String?){       val allCaps  = s?.toUpperCase()    println(allCaps)} printAllCaps("abd") //输出:ABD printAllCaps(null) //输出:null   也可以多个安全调用链接在一起
class Address(val streetAddress : String, val zipCode : Int, val city : String, val country : String)class Company(val name : String, val address : Address?)class PersonT(val name : String, val company : Company?)fun PersonT.countryName() : String{       val country = this.company?.address?.country    return if(country != null) country else "Unknown"}//调用:val person = PersonT("liSuo",null)println(person.countryName())//输出:Unknown   解释:null合并运算符
demo:
fun strLen(s : String?) : Int{       return s?.length ?: 0}//调用: strLen(null) //输出: 0   解释:as?尝试把值转换为指定的类型,如果值不是合适的类型就返回null
解释:可以 把任何值转换为非空类型。如果对null值做非空断言,会抛出异常。
let函数所作的事情就是把一个调用它的对象变成lambda表达式的参数。
demo:
结合安全调用语法,把调用let函数的可空对象,转换为非空类型。
fun strLen(s : String) : Int{       return s.length}val x : String? = nullx?.let {    strLen(x) }   如上的函数只在x值非空时才会调用。
可以使用lateinit修饰符来指定延迟初始化。
class MyService{       fun performAction() : String = "foo"}class MyTest{       private lateinit var myService: MyService   //声明一个不需要初始化器的非空类型属性    fun setUp(){           myService = MyService()    }    fun testAction(){           println(myService.performAction())    }}   注意:
延迟初始化的属性都是var,因为需要在构造方法外修改它的值。
Kotlin要求你在构造方法中初始化所有属性。如果某个属性是非空类型,就必须提供非空的初始化值。否则,就必须使用可空类型。
如下类中定义是错误的。
class MyTest{       private  var myService: MyService }   尽管这个属性是非空类型,但是因为是延迟初始化的属性,你不需要在构造方法中初始化它。
如果在属性被初始化之前调用了它,会出现异常。
Exception in thread “main” kotlin.UninitializedPropertyAccessException: lateinit property myService has not been initialized
at dataUtil.MyTest.testAction(TypeSystem.kt:33) at Kotlin.Kotlin的类型系统Kt.main(Kotlin的类型系统.kt:19)
可以为一个类定义扩展函数来检查可空性。
相似的实现:isEmpty,isBlank,isEmptyOrNull,isNullOrBlank
fun strLen(s : String?) : Int{       if(!s.isNullOrEmpty()){          return s.length    }    return 0}   Kotlin中所有泛型类和泛型函数的类型参数默认都是可空的。
funprintHashcode(t : T){ println(t.hashCode())}printHashcode(null)//输出0 
要使类型参数非空,要为它指定一个非空的上界。如下泛型会拒绝可空值作为实参。
funprintHashcode(t : T){ println(t.hashCode())}printHashcode(null)//此时编译器直接报错 
java中使用注解来表达可空性。

Kotlin可以识别多种不同风格的可空性注解。但是当这些注解不存在时,java类型会变成Kotlin中的平台类型。
平台类型
平台类型本质上就是Kotlin不知道可空性信息的类型。既可以把它当作可空类型处理,也可以当作非空类型处理。

注意:在实现java类或接口的方法时一定要搞清楚其可空性。因为方法的实现可以在非Kotlin的代码中被调用。Kotlin编译器会为你声明的每一个非空的参数生成非空断言
demo:
/*java*/public class Person3 {       private final String name;    public Person3(String name) {           this.name = name;    }    public String getName() {           return name;    }}   此时Kotlin编译器完全不知道String类型的可空性,所以必须自己处理。如果不处理,在遇到null时,会报错
fun yellAt(person3: Person3){       println(person3.name.toUpperCase())}//调用:val person3  = Person3(null)yellAt(person3)//结果:Exception in thread "main" java.lang.NullPointerException: person3.name must not be null	at dataUtil.TypeSystemKt.yellAt(TypeSystem.kt:45)	at Kotlin.Kotlin的类型系统Kt.main(Kotlin的类型系统.kt:22)   Kotlin中的可空类型不能用java的基本数据类型表示,因为null只能存储在Java的引用类型的变量中。所以用了基本类型的可空类型后,它就会编译为对应的包装类型。
Kotlin不会自动把数字从一种类型转换到另一种类型。必须显式地进行转换。每一种基本数据都定义有转换函数:toByte(),toShort(),toChar()等。并且它们都支持双向转换。
在用equals比较装箱值的时候,不仅会检查他们存储的值,还会比较装箱的类型。
当书写字面值时,一般不需要使用转换函数。必要的转换会自动发生。
算数运算符也被重载了,它们可以接收所有适合的数字类型。
demo:
//下面的是可以被正常编译的val b : Byte = 1val l = b + 1Lfoo(l)
Kotlin算数运算符关于数值范围溢出的行为和Java完全一致。
Kotlin中的Unit类型完成了Java中的void一样的功能。当函数没什么有意义的结果返回时,它可以用作函数的返回类型。
fun  f():Unit{     } 不带返回类型声明的函数是内部把显示的Unit声明省略了
Kotlin中的Unit和Java中的void的不同点:
Unit是一个完备的类型,可以作为类型参数,而void却不行。只存在一个值是Unit类型,这个值也叫做Unit,并且在函数中会被隐式的返回。
demo:
interface Processor{ fun process() : T}class NoResultProcessor : Processor { override fun process() { //返回类型是Unit,可以省略类型的说明 //这里不需要显式的return,编译器会隐式的加上reutrn Unit }} 
Nothing函数没有任何值,只有被当作函数返回值使用,或者被当作泛型函数返回值类型参数使用才有意义。
使用场景:对于某些函数来说,它们从来不会成功的结束,此时可以用Nothing类型作为返回值。
测试库中的fali函数:通过抛出异常来让测试失败。
fun fail(message : String) : Nothing{           throw IllegalArgumentException(message)} 一个包含无限循环的函数。
fun arrayRun() : Nothing {           while (true) {           }} list集合的三种类型
| 集合 | 解释 | 
|---|---|
| List | 列表不为null,并且列表中的每个值不为null | 
| List<Int?> | 列表不为null,但列表中的每个值可以为null | 
| List<Int?>? | 列表可以为null,列表中的每个值也可以为null | 
filterNotNull函数:遍历一个包含可空值的集合并过滤掉null(返回类型对于为上面的第一种)
Kotlin的集合设计把访问集合数据的接口和修改数据的接口分开了。

demo:
funcopyElements(source : Collection ,target : MutableCollection ){ for(item in source){ target.add(item) }} 
只读集合不一定是不可变的。
如果一个集合对象被只读的和可变的同时引用。那么你正在使用集合的时候它可能会被其他代码修改。

如果出现上面的情况,会导致concurrentModificationException 错误和其他一些问题。
所以:只读集合并不总是线程安全的


在Java代码中调用Kotlin中的集合会对集合的可变性产生重要影响。因为Java并不会区分只读集合与可变集合。即使Kotlin中把集合声明成只读的,Java代码也能够修改这个集合。
demo:

Kotlin中的数组是一个带有类型参数的类。
要在Kotlin中创建数组,可以用以下的方法。
Kotlin 中 Array 类就像普通的泛型类 但它会被编译成 Java 数组
转载地址:http://fhpxz.baihongyu.com/