开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 14天,点击查看活动详情
表达式和块体
表达式和块体的区别是:表达式应该是等于后面加表达式,而块体后面是大括号。
表达式会自己推断返回类型不需要显示指定。 而方法不行,方法需要明确使用返回值类型。而且表达式中不能用return,方法强制要求return如果有返回值。 如果等号后面加上大括号,那么他将会被认为是匿名函数也就是lambda表达式。 如果等号明确了返回值声明也可以认为是表达式,但是如果后面加了块体括号那么编译期间就会报错。 因此表达式可以声明返回值类型不一定强制依赖编译器的类型推断,但是就不能再用大括号了(没有类型用大括号被认为是lambda)
值的比较
java比较值使用equals,比较引用使用==。 在kotlin中比较值使用==,比较引用使用===。 不同的是对于比较值来说java的equals需要判断对象是否为空,而kotlin的==泽不需要。kotlin的==会先对对象进行判空在调用equals方法
默认参数和vararg可变长度参数还有 spread展开用于配合可变长度参数时传递的类型是数组进行转换。 解构可以直接定义多个变量一次性从方法返回的数据类中拿到值,js的解构是按照 名称来解构的,而kotlin的解构是按返回的数据类中构造函数创建的参数顺序进行赋值的。 对于不想要赋值的变量使用下划线即可跳过
短路求值定义: 如果一个表达式中用到了&&运算符,左侧的条件如果不符合那么就不会调用右侧的表达式(对于|则是反过来)。java的三元运算符在kotlin中使用?.(不可空才调用)和?:(为空才调用)表示
Java,KT泛型大不同
泛型是类型安全的,这在JAVA当中是正常的。如果泛型参数在实际使用的过程中用的却是泛型参数的派生类或者基类,那么会在编译阶段报出类型不匹配错误 原因是因为如果传递的集合是派生类的话,如果在声明处想要再次添加其他的派生类的时候,这会出现一些错误。 比如狗和猫都是继承动物,但是在声明动物的泛型中实际传递的引用是狗类型,对于狗类型的方法可以调用成功,但是如果之后传递的是猫呢?就可能会报出狗类方法不存在猫中的错误 继承带来的替换会产生一些让人摸不着头脑的问题。 在kotlin当中有一种集合是不可变集合,也就是list。对于这种集合来说,它是不可变的,因此它可以使用派生类或者子类(因为他后续不会对其进行修改也就不会出现上面那个例子的错误,集合的元素是固定的在编译器就回进行检查是否正确)。
声明式协变
声明式协变指的是在声明的时候就已经指定了不能对其进行修改。 比如list接口就是list不可能被修改因为他是不可变的,但是mutilblist就不行了,他的定义是可以进行修改的只能在使用的时候才进行判断是否调用了设置的方法 因此如果对于本身就不可变的list接口来说天然的就不允许变化因此也叫声明式协变
泛型包括读取和设置两种行为,当使用协变时设置行为不能使用(也就是调用方法),如果可以设置那么声明为基类的泛型就可以传递任意实现类,这是不可控的因为子类会重写父类的方法。
语法out代表协变,允许传递其派生类的类型,但是Kotlin将断言没有对允许传入数据的from引用进行任何方法调用,这个是通过kotlin检查被调用方法的签名来检测是否调用out类型的方法。
泛型的具体化
对于编写aidl通信文件,refired并不能直接使用(aidl什么时候支持kotlin的语法),如果只在可以直接跳转到对应代码的文件才可以使用具体化参数类型功能。 对于外部通信文件还是需要将class类型参数单独专门作为一个参数进行传递,在函数内部通过class类的isinstance函数来判断是否属于某个class的实力;通过cast方法进行强转为某个类型 具体化类型参数之后在函数内部可直接使用t的类型,而java会在编译时进行类型擦除也就是在掉用处编译器会自动追加强转的代码。 泛型具体化的实现: 也就是说具体的类型函数内部不知道,kotlin通过函数内联(将函数的方法体函数块拼接到调用方,这样就知道掉用处传递的具体的类型是什么)可以实现将函数内部多携带一个类型参数class 函数内联
Kotlin的泛型星投影
如果传入的是一个集合类型的泛型,在函数内部可能会不经意的设置集合元素或者修改元素,这就带来了不确定性和不安全性。 因此使用星号可以限制对泛型参数的任何写入操作。限制写入操作和逆变的作用很像但是写起来简单而且也不需要规定传入的泛型类型约束。
java和kotlin泛型的区别
1.java中不支持声明点协变和声明点逆变,也就是list接口。java中都是使用过程中才进行规定斜边和逆变而不是创建类的过程中就指定了 2.java中使用?代替可以接受任何类型但是只带有只读约束的泛型对象。kotlin通过星投影来实现同样的效果。
泛型类型约束
约束是指传递的泛型是某个类型的子类或者实现了某个接口。 对于复杂的泛型约束来说,泛型约束应该在方法声明的最后也就是小括号之后 加上where语句 并规定 泛型类需要实现哪些接口 多个约束之间用逗号分隔
关于泛型中 out和 in 的思考
out允许读取值劝却不允许调用方法,首先对于取值来说获取的属性都是父类的因此可以获取,但是对于方法调用来说子类有自己的方法也有自己的私有方法,没有权限访问。因此不支持调用方法行为 in是允许调用方法(因为最后调用的方法指针就是传入进来的这个最后的子类)但是不允许读取值(如果要获取的属性是父类的私有属性那么没有权限获取)。
Kotlin带来的is自动转换
使用is自动转换后在调用方法这是防御性编程,因为如果is返回false那么就代表不是该类形。 但是这种方法有个缺点就是需要多套个if在进行调用,行数会变多。如果调用的方法不是特别多的情况下使用这种就增加了行数,相反使用as就可以直接进行强转而不需要if判断后再转 如果强转成功那么返回对应类型,如果失败那么将得到一个类型转换异常。 使用as?当强转失败时将会受到null,而不是异常,当调用方法特别多的情况而且需要进行类型判断的分支节点条件比较多的情况下使用is,否则如果只是调用一行代码的话还是使用as代码行数更少
Nothing
Nothing类没有实例,它表示一个永远不存在的值或结果。当用作方法的返回类型时,这意味着函数永远不会返回——函数调用只会导致异常。 Nothing的一个独特能力是它可以代表任何东西——也就是说,Nothing可以替代任何类, 函数调用抛异常时,会使用Nothing来表示。 一般用作返回值的类型安全判断,比如一般情况下函数返回null要加问号,但是如果返回异常则不需要加?。 Nothing表示调用者会收到一个异常,同样当调用着收到异常时代表返回了Nothing
Kotlin中的基元类型集合
不可变集合中本质上是Array数组类中的ArrayList内部类 array数组是可以转换成列表集合的,但是转换后的这个集合列表不允许变(很正常,如果需要添加可变的添加和删除借口,那么添加超过需要扩容还需要重新排列,删除还需要在之后的元素重新往前排太复杂) 因此对于不可变集合来说其本质上就是用的java对数组转换为集合的类型 对于普通的arrayOf中使用的类型来说如果传入的类型是基本类型则会进行自动的装箱和拆箱操作(在实际应用中也会发现传入int可以自动使用Integer的方法),如果不需要使用这些方法避免掉装箱拆箱的消耗那么使用特有的基本类型数组方法表示即可: 如intarrayof 如果使用基元类型也就是八大数据类型时,如果没有指定intarray那么创建出来的底层java类是integer类型的数组,kotlin会默认转换成integer的数组。 所以使用基元类型对数组时需要看是否需要装箱类的方法若没有必要则使用intarrayof,创建int类型对数组。 为什么?因为如果你之后存储int的时候都需要创建integer的实例。如果数组元素过多,则会导致大量对象
Kotlin中的不可变和可变集合
kotlin提供了可变和不可变集合两种类型,对于java来说虽然后面加了不可变集合,但是实现的却是可变集合的接口,因此对于不可变集合来说也可以调用add方法,只不过会在运行时抛出异常(一点也不友好) 但是对于kotlin来说他将错误警告信息提前到了编译时期
Pair
pair的用法,拜托这样写的代码真的好优雅,对一个数组里面的值进行处理变成一组对象。 如果创建的是一对对像,对于类型相同的值来说使用数组即可,但是如果用的是不同数据类型那么就需要用object来做保存,当然可以使用一个单独的类保存但是需要创建单独的类作为开销。 原来我一直在使用pair这个类啊哈哈,确实map是一对一的对象集合,正好就是pair作用。 使用to中缀表达式可以省略点和括号传参,真的很棒
内联类
传递内联类在运行时的表现: 传递内联类的地方将会被替换成基元类型,但是在函数调用的时候编译器将验证是不是对应内联类的实例! 如果是那么讲展开实例传递给方法,删除了包装基元数据以及在内存对象创建方面的开销: 为什么?写的时候还是需要传递实例的,但是并不是真正的创建对象而是在编译器验证成功后对内联类的属性进行直接展开不需要创建内联类的对象
数据类的解构行为
契约式设计。接口作为规范,类作为规范的实现者。抽象类也可以作为契约的规范,子类用于契约的实现者 解构第几个属性就是调用compentn方法,比如获取第一个属性就是调用compemt1方法获取该属性 数据累的compentn方法可以进行解构操作。 对于需要跳过的属性可以使用下划线代替,而且解构的属性只可以获取主构造函数的属性且顺序是主构造函数中属性声明的属性
Kotlin中的sealed密封类
sealed密封类 的构造函数是单利的,他的构造方法是私有的,并且继承密封类只能从 同一个文件派生不能在其他文件继承密封类(新版本中去除了这个同个文件集成的限制) sealed密封类的诞生目的: 有的时候我们希望类是可以被继承的有些是不可以被继承的,但是还有一种情况我们希望某些类只能有固定几种派生类。这个在模型匹配中非常好用搭配when表达式不需要编写额外的else分支而且也更加安全