本文共 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/