博客
关于我
Kotlin学习篇(4)—— Kotlin的类型系统
阅读量:608 次
发布时间:2019-03-12

本文共 6406 字,大约阅读时间需要 21 分钟。

目录

5. Kotlin的类型系统

5.1 可空性

5.1.1 可空类型

5.1.1.1 : ?

问好可以加在任何类型的后面来表示这个类型的变量可以存储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)   //合法的代码

5.1.1.2 :安全调用运算符 ?.

解释:允许把一次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

5.1.1.3 :Elvis运算符 ?:

解释:null合并运算符

demo:

fun strLen(s : String?) : Int{       return s?.length ?: 0}//调用: strLen(null) //输出: 0

5.1.1.3 :安全转换 as?

解释:as?尝试把值转换为指定的类型,如果值不是合适的类型就返回null

5.1.1.4 :非空断言 !!

解释:可以 把任何值转换为非空类型。如果对null值做非空断言,会抛出异常。

5.1.1.5 :“ let ”函数

let函数所作的事情就是把一个调用它的对象变成lambda表达式的参数。

demo:

结合安全调用语法,把调用let函数的可空对象,转换为非空类型。

fun strLen(s : String) : Int{       return s.length}val x : String? = nullx?.let {    strLen(x) }

如上的函数只在x值非空时才会调用。

5.1.2 延迟初始化的属性

可以使用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)

5.1.3 可空类型的扩展

可以为一个类定义扩展函数来检查可空性。

相似的实现:isEmpty,isBlank,isEmptyOrNull,isNullOrBlank

fun strLen(s : String?) : Int{       if(!s.isNullOrEmpty()){          return s.length    }    return 0}

5.1.4 参数类型的可空性

  • Kotlin中所有泛型类和泛型函数的类型参数默认都是可空的。

    fun
    printHashcode(t : T){ println(t.hashCode())}printHashcode(null)//输出0
  • 要使类型参数非空,要为它指定一个非空的上界。如下泛型会拒绝可空值作为实参。

    fun
    printHashcode(t : T){ println(t.hashCode())}printHashcode(null)//此时编译器直接报错

5.1.5 可空性和java

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)

5.2 基本数据类型和其他数据类型

5.2.1 基本数据类型: Int, Boolead 及 其它。

  • Kotlin不区分基本数据类型和包装类型。你使用的永远是同一类型。
  • 当在Kotlin中使用java声明时,java的基本类型会变成非空类型,因为他们都不能持有null值。
  • 对于不同的情境,Kotlin在编译时对类型的表示是不同的。比如对于Int,
    • 如果它没用作变量,属性,参数,和返回类型,会被编译成Java基本数据类型int
    • 如果用于泛型类型参数的基本数据类型会被编译成对应的Java包装类型。

5.2.2 可空的基本数据类型:Int?,Boolean? 及 其他

Kotlin中的可空类型不能用java的基本数据类型表示,因为null只能存储在Java的引用类型的变量中。所以用了基本类型的可空类型后,它就会编译为对应的包装类型。

5.2.3 数字转换

  • Kotlin不会自动把数字从一种类型转换到另一种类型。必须显式地进行转换。每一种基本数据都定义有转换函数:toByte(),toShort(),toChar()等。并且它们都支持双向转换。

  • 在用equals比较装箱值的时候,不仅会检查他们存储的值,还会比较装箱的类型。

  • 当书写字面值时,一般不需要使用转换函数。必要的转换会自动发生。

  • 算数运算符也被重载了,它们可以接收所有适合的数字类型。

    demo:

    //下面的是可以被正常编译的val b : Byte = 1val l = b + 1Lfoo(l)
  • Kotlin算数运算符关于数值范围溢出的行为和Java完全一致。

5.2.4 “Any”和“Any?”:根类型

  • Any类型是Kotlin中所有非空类型的超类型。
  • 把基本数据类型的值赋给Any类型的变量是会自动装箱。
  • Any是非空类型,如果你需要可以持有null类型的变量,必须使用Any?类型。
  • 在Kotlin中使用Any时,它会被编译成Java字节码中的Object。

5.2.5 Unit类型:Kotlin中的“void”

  • 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 }}

5.2.6 Nothing类型:“这个函数永不返回”

  • Nothing函数没有任何值,只有被当作函数返回值使用,或者被当作泛型函数返回值类型参数使用才有意义。

  • 使用场景:对于某些函数来说,它们从来不会成功的结束,此时可以用Nothing类型作为返回值。

    • 测试库中的fali函数:通过抛出异常来让测试失败。

      fun fail(message : String) : Nothing{           throw IllegalArgumentException(message)}
    • 一个包含无限循环的函数。

      fun arrayRun() : Nothing {           while (true) {           }}

5.3 集合

  • list集合的三种类型

    集合 解释
    List 列表不为null,并且列表中的每个值不为null
    List<Int?> 列表不为null,但列表中的每个值可以为null
    List<Int?>? 列表可以为null,列表中的每个值也可以为null
  • filterNotNull函数:遍历一个包含可空值的集合并过滤掉null(返回类型对于为上面的第一种)

5.3.1 只读集合和可变集合

  • Kotlin的集合设计把访问集合数据的接口和修改数据的接口分开了。

    • Kotlin.collections.Collection,使用这个接口,可以遍历集合,获取集合大小等,但没有任何添加或者移除元素的方法。
    • Kotlin.collections.MutableCollection接口,继承了上面的接口,提供了方法来添加和移除元素,清空集合等。

    在这里插入图片描述

    demo:

    fun 
    copyElements(source : Collection
    ,target : MutableCollection
    ){ for(item in source){ target.add(item) }}
  • 只读集合不一定是不可变的。

    • 如果一个集合对象被只读的和可变的同时引用。那么你正在使用集合的时候它可能会被其他代码修改。

      在这里插入图片描述

    • 如果出现上面的情况,会导致concurrentModificationException 错误和其他一些问题。

    • 所以:只读集合并不总是线程安全的

5.3.2 Kotlin集合和java

  • 每一种Java集合接口在Kotlin中都有两种表示:一种是只读的,另一种是可变的。

在这里插入图片描述

  • 除了集合之外,Kotlin中的Map类也被表示成了两种不同的版本:Map和MutableMap

在这里插入图片描述

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

    demo:

    在这里插入图片描述

5.3.3 数组

Kotlin中的数组是一个带有类型参数的类。

要在Kotlin中创建数组,可以用以下的方法。

  • arrayOf函数来创建一个数组
  • arrayofNulls创建一个给定大小的数组,包含的是null元素。
  • Array构造方法接收数组的大小和一个lambda表达式,用lambda来创建函数

Kotlin 中 Array 类就像普通的泛型类 但它会被编译成 Java 数组

转载地址:http://fhpxz.baihongyu.com/

你可能感兴趣的文章
mysql 断电数据损坏,无法启动
查看>>
MySQL 日期时间类型的选择
查看>>
Mysql 时间操作(当天,昨天,7天,30天,半年,全年,季度)
查看>>
MySQL 是如何加锁的?
查看>>
MySQL 是怎样运行的 - InnoDB数据页结构
查看>>
mysql 更新子表_mysql 在update中实现子查询的方式
查看>>
MySQL 有什么优点?
查看>>
mysql 权限整理记录
查看>>
mysql 权限登录问题:ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)
查看>>
MYSQL 查看最大连接数和修改最大连接数
查看>>
MySQL 查看有哪些表
查看>>
mysql 查看锁_阿里/美团/字节面试官必问的Mysql锁机制,你真的明白吗
查看>>
MySql 查询以逗号分隔的字符串的方法(正则)
查看>>
MySQL 查询优化:提速查询效率的13大秘籍(避免使用SELECT 、分页查询的优化、合理使用连接、子查询的优化)(上)
查看>>
mysql 查询数据库所有表的字段信息
查看>>
【Java基础】什么是面向对象?
查看>>
mysql 查询,正数降序排序,负数升序排序
查看>>
MySQL 树形结构 根据指定节点 获取其下属的所有子节点(包含路径上的枝干节点和叶子节点)...
查看>>
mysql 死锁 Deadlock found when trying to get lock; try restarting transaction
查看>>
mysql 死锁(先delete 后insert)日志分析
查看>>