title: Kotlin语法
date: 2020-08-23 11:43
category: Kotlin
tags: Kotlin
基本数据类型
类型 | 位宽 |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
字面常量
常见的类型的字面量常量
- 十进制: 124
- 长整型以大写的L结尾: 123L
- 16进制以0x开头:0x0F
- 2进制以0b开头:0b0000001010
- 注意:8进制不支持
Kotlin同时也支持传统符号表示的浮点数值
- Doubles 默认写法: 123.4, 123.4e10
- Floats 使用f或者F后缀:124.5f
val aa = 1_000_000
val cc = 1235_455_2224_55L
val ca = 999_22_1111L
val hexBytes = 0xFF_EC_DE_3F
val bytes = 0b10101001_10101_101011
比较两个数字
Kotlin中没有基础数据类型,只有封装的数字类型,定义的变量,Kotlin会将其封装成一个对象,
这样可以保证不会出现空指针.数字类型也一样,所以在比较两个数字的时候,就是比较两个数据的大小和地址是否相同.(equals, hashcode)
===
表示比较对象地址,==
表示比较两个值大小
fun main(arg:Array<String>){
val a:Int = 1000
println(a===a) // true 值相等,对象地址相等
//经过装箱,创建了两个不同的对象
val boxedA : Int? =a
val autoBoxedA :Int? =a
//经过了装箱(就是将基本数据类型包装成一个对象)
// 但是值是相等的
println(boxedA == autoBoxedA) // true 值相等
println(boxedA === autoBoxedA) //false 值相等,但是对象地址不一样
}
类型转换
由于不同的表示方式, 较小类型并不是较大类型的子类,较小类型不能隐式转换为较大的类型,这就意味着在不进行显示转换情况下.不能把Byte型值赋值给一个Int变量.
但是Java中不能将Int值赋值给一个Byte
int b=1;
byte a =(byte)b; // 较大类型的不能直接赋值给较小类型,区间范围不一致,只能通过将较大类型向下强转为较小类型
kotlin的规则:
val b : Byte =1 // ok,字面量是静态检测
val i : Int = b // 错误
//使用toInt方法来解决
val b:Byte=1
val i:Int =b.toInt()
// 小的类型是不能直接赋值给大类型的Kotlin里面要求的,但是在Java中小类型是可以直接赋值给大类型数据
toByte():Byte
toShort():Short
toInt():Int
toLong():Long
toFloat():Float
toDouble():Double
toChar():Char
自动类型转化:
val l = 1L+3 //Long + Int = > Long
位操作符
对于Int和Long类型,还有相关的位操作法可以使用
shl(bits) -- >左移位(Java <<)
shr(bits) --->右移位(Java >>)
ushr(bits) ---> 无符号右移(Java >>>)
and(bits) - 与
or(bits) - 或
xor(bits) - 异或
inv() - 反向
字符
和Java不一样,Kotlin中的Char不能直接和数字操作,Char必须是单引号‘包含起来,比如普通的字符’0’,‘a’
fun check(c:Char){
if(c==1){
// 错误,类型不兼容
}
}
数组
数组用类Array实现, 并且还有一个size属性以及get和set方法;
创建方式: 一种是使用函数arrayOf();另外一种是使用工厂函数
fun main(args:Array<String>){
// [1,2,3]
val a = arrayOf(1,2,3)
//[0,2,4]
val b = Array(3,{
i->(i*2)})
//读取数组内容
println(a[0]) // 1
println(b[2]) // 4
}
协变 与 逆变
Kotlin 中没有像Java一样的<? extends T>
这样的方式, 也没有父类向子类的转换,但是为了数据的安全性,提出了协变与逆变的说法:
概念:
协变: A是B的子类型,并且Generic也是Generic的子类型,那么Generic可以被称之为一个协变类.
Java上界通配符<? extends T>
Java的协变通过上界通配符实现,如果Dog是Animal的子类,但是List并不是List的子类.
List<Animal> animals = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();
animals = dogs; // 编译不通过
使用上界通配符之后,List<Dog>
变成了List<? extends Animal>
的子类型,即animals可以放入任何Animal以及子类的List;
List<? extends Animal> animals = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();
animals = dogs; // 将Animal的子类型列表放入到animlas
Kotlin的关键字out
fun main(){
var animals : List<Animal> = ArrayList()
var dogs = ArrayList<Dog>()
animals = dogs // why compile success
}
// Kotlin的List源码中使用了out,out相当于是Java上界通配符
public interface List<out E> : Collection<E> {
//....
override val size:Int
override fun contains(element:@UnsafeVariance E):Boolean
override fun containsAll(elements:Collection<@UnsafeVariance E>):Boolean
}
类型的参数使用了out之后,该参数只能出现在方法的返回类型.
@UnsafeVariance修饰泛型E 打破了什么限制??
逆变: 如果A是B的子类型,并且Generic是Generic的子类型, 那么Generic可以被称之为逆变类
Java的逆变是通过下界通配符实现的,<? super T>
List<? extends Animal> animals = new ArrayList<>();
// 上界通配符 协变 是无法添加新的对象的. 编译器只能知道类型是Animal的子类,
// 并不能确定具体类型是什么,因此无法验证类型的安全性.
animals.add(new Dog()); // compile error
使用下界通配符之后,代码编译通过
List<? super Animal> animals = new ArrayList<>();
animals.add(