国外界面设计网站,重庆装修协会,做外贸需关注的网站,问答社区网站建设文章目录 数字字面常量显式转换数值类型转换背后 位运算符字符串字符串模板修饰符数组集合#xff08;Kotlin自带#xff09;通过序列提高效率惰性求值序列的操作方式中间操作末端操作 可null类型安全调用操作符 ?.操作符 ?:非空断言操作符 !! 使用类型检测及自动类型转换安… 文章目录 数字字面常量显式转换数值类型转换背后 位运算符字符串字符串模板修饰符数组集合Kotlin自带通过序列提高效率惰性求值序列的操作方式中间操作末端操作 可null类型安全调用操作符 ?.操作符 ?:非空断言操作符 !! 使用类型检测及自动类型转换安全的类型转换 as?返回和跳转 在Kotlin中所有东西都是对象在这个意义上讲我们可以在任何变量上调用成员函数和属性。 一些java中的基本数据类型可以有特殊的内部表示——例如数字(int)、字符和布尔值可以在运行时表示为原生类型值
数字
Kotlin处理数字在某种程度上接近Java,但是并不完全相同。例如对于数字没有隐式拓宽转换如Java中int可以隐式转换为long) 另外有些情况的字面值略有不同。
Kotlin提供了如下的内置类型来表示:
Byte字节1字节 Short短整型2字节 Int整型4字节 Long长整型8字节 Float单精度浮点型4字节 Double双精度浮点型8字节 Boolean布尔型1字节 Char字符型2字节
注意在Kotlin中字符不是数字,字符用Char类型表示。它们不能直接当作数字可以用char.code表示出对应编码 val char cprint(char.code)//99字面常量
可以使用下划线使数字常量更易读,并且不影响赋值
val int 1_0000_0000
print(int / 10000)
//10000还可以
val LongNumber 999_99_9999L
val hexBytes 0xFF_EC_DE_5E
val bytes 0b11010010_01101001_10010100_10010010显式转换
由于不同的表示方式较小类型并不是较大类型的子类型。这代表他们不能进行直接的隐式转换 我们可以显式转换来拓宽数字
fun main(){val int 1_0000_0000val long: Long int.toLong()
}数值类型转换背后
var x 5 // 这行代码创建了一个Int类型的变量x以及一个Int类型值为5的对象。x保存了该对象的引用
var z : Long x.toLong() // 这行代码创建了一个新的Long变量z。x对象的toLong()函数被调用并且创建了一个值为5的Long对象该Long对象的引用被存储在z中如果该数值超出了新对象所能存储的范围该怎么办 就像一大杯水装进一个小杯有些会溢出
如果Long的值超出了Int能容纳的范围那么编译器将会舍弃超出的部分会得到一个奇怪仍可计算的数值。例如:
var x 1234567890123
var y: Int x.toInt()
println(y) // 1912276171(C语言初学者应该很熟悉的问题)
位运算符
(只用于Int和Long。Kotlin的特性可以重写操作符函数的逻辑):
shl(bits) – 有符号左移 (Java 的 )
shr(bits) – 有符号右移 (Java 的 )
ushr(bits) – 无符号右移 (Java 的 )
and(bits) – 位与
or(bits) – 位或
xor(bits) – 位异或
inv() – 位非
区间实例以及区间检测a..b、 x in a..b、 x !in a..b字符串
字符串的元素——字符Char可以使用索引运算符访问:str[i]。可以用for循环迭代字符串:
for (c in str) {println(c)
}当你使用双引号“表示字符串时它只能包含单行文本。 而当你使用三个双引号”“”表示字符串时它可以包含多行文本 转义采用传统的反斜杠方式。 原生字符串 使用三个引号分界符括起来内部没有转义并且可以包含换行和任何其他字符:
val text for (c in foo)print(c)字符串模板
字符串可以包含模板表达式即一些小段代码会求值并把结果合并到字符串中。模板表达式以美元符$开头再加上变量的名字。
val i 10
val s i $i // 求值结果为 i 10字符串判等 Kotlin中的判等性主要有两种类型
结构相等。通过操作符来判断两个对象的内容是否相等。引用相等。引用相等由双等号以及其否定形式! 操作判断。a b当且仅当a和b指向同一个对象时求值为true。如果比较的是运行时的原始类型比如Int那么’ 判断的效果也等价于’。
var a hello
var b hello
var c Kotlin
var d Kot
var e lin
var f d e
a b // true
a b // true
c f // true
c f // false结构相等由 以及其否定形式! 操作判断。 那么a b这样的表达式会被翻译为 a?.equals(b) ?: (b null) 也就是说如果a不是null则调用equals(Any?)函数否则即a是null检查b是否与null引用相等。 精妙 好多等号一直显示高亮
修饰符
注意在kotlin中默认的修饰符是public
private 所以如果我们给一个类声明为private 我们就不能在定义这个类之外的文件中使用它。 另一方面如果我们在一个类里面使用了private修饰符那访问权限就被限制在这个类里面了。甚至是继承这个类的子类也不能使用它。protected 在Java中是包、类及子类可访问而在Kotlin中只允许类及子类。internal 它与Java的default有点像但也有所区别。如果是一个定义为internal的包成员的话对所在的整个module可见。如果它是一个其它领域的成员它就需要依赖那个领域的可见性了。 比如如果写了一个private类那么它的internal修饰的函数的可见性就会限制与它所在的这个类的可见性。public 这是最没有限制的修饰符。这是默认的修饰符成员在任何地方被修饰为public很明显它只限制于它的领域。
数组
和java不同的是新建一个数组用类Array实现并且还有一个size属性及get和set万物皆对象由于kotlin使用[]重载了get和set方法所以我们可以通过下标很方便的获取或者 设置数组对应位置的值。 Kotlin标准库提供了arrayOf()创建数组和xxArrayOf创建特定类型数组
val myArray arrayOf(1, 2, 3)还有
val nums arrayOf(1, 2, 3)
val numbers intArrayOf(10, 20, 30)
//intArrayOf 新建int类型数组
val array1 Array(10, { k - k * k })
//可用表达式
val emptyArray emptyArrayString()
//括号内可以填参数表示新建数组的大小和Java不一样的Kotlin的数组是容器类提供了ByteArray,CharArray,ShortArray,IntArray,LongArray,BooleanArray,FloatArray和DoubleArray 那么以下两个有什么区别呢
val arrayA arrayOf(1, 2, 3)
val arrayB intArrayOf(1, 2, 3)当我们显式指明数据类型后
val arrayA: ArrayInt arrayOf(1, 2, 3)
val arrayB: IntArray intArrayOf(1, 2, 3)区别注意一下arrayOf() 是Kotlin自带的很便携可以进行自判断类型生成对应的Array并填写泛型类。 但是Array是会使用装箱技术。它使用对象引用来存储每个整数或其他基本数据类型因此它的内存占用比较大。而IntArray由于不需要装箱和拆箱的过程IntArray拥有更好的性能和更高的效率。但是也有一定的缺点他比Array的api要少…
集合Kotlin自带
Kotlin有三个主要的集合类型List、Set和Map也可以分为可变集合和不可变集合//反正用得不多 简单的List、Set和Map是不可变的这意味着集合被初始化后不能再添加或移除元素。如果想要添加或移除元素Kotlin提供了可变的子类型作为替代方案MutableList、MutableSet和MutableMap。因此如果想要利用List的所有优势并希望能够更新其内容请使用MutableList。 Kotlin的Listout T类型是一个提供只读操作如size、get等的接口。和Java类似它继承自CollectionT进而继承自IterableT。 改变list的方法是由MutableListT加入的且适用于Set和Map。 可变集合顾名思义就是可以改变的集合。可变集合都会有一个修饰前缀“Mutable”比如MutableList。这里的改变是指改变集合中的元素比如以下可变集合: val list mutableListOf(1, 2, 3, 4, 5)
list[0] 0 // 变成[0, 2, 3, 4, 5]Kotlin没有专门的语法结构创建list或set。要用标准库的方法如listOf()、mutableListOf()、setOf()、mutableSetOf()。 创建map可以用mapOf(a to b, c to d)。 最主要可以用 map[key] value 我很喜欢这样用
var list listOf(“a”, “b”, “c”) for(c in list) { println© }
fun main() {var map TreeMapString, String()map[0] 0 hahamap[1] 1 hahamap[2] 2 hahaprintln(map[1])
}val numbers: MutableListInt mutableListOf(1, 2, 3)
val readOnlyView: ListInt numbers
println(numbers) // [1, 2, 3]
numbers.add(4)
println(readOnlyView) // [1, 2, 3, 4]
readOnlyView.clear() // - 不能编译
val strings hashSetOf(a, b, c, c)Kotlin中提供了很多操作结合的函数例如:
val newList list.map{it * 2} // 对集合遍历在遍历过程中给每个元素都乘以2得到一个新的集合
val mStudents students.filter{it.sex m} // 筛选出性别为男的学生
val scoreTotal students.sumBy{it.score} // 拥挤和中的sumby实现求和通过序列提高效率 因为filter方法和map方法都会返回一个新的集合也就是说会产生两个临时集合(跟当初javaString一样)因为list会先调用filter方法然后产生的集合会再次调用map方法。如果list中的元素非常多这将会是一笔不小的开销。为了解决这一问题序列(Sequence)就出现了。(Builder) list.asSequence().filter {it 2}.map {it * 2}.toList()首先通过asSequence方法将一个列表转换为序列然后在这个序列上进行相应的操作最后通过toList方法将序列转为列表。将list转换为序列在很大程度上就提高了上面操作集合的效率。
因为在使用序列的时候filter方法和map方法的操作都没有创建额外的集合这样当集合中的元素数量巨大的时候就减少了大部分开销。
在Kotlin中序列中元素的求值是惰性的这就意味着在利用序列进行链式求值的时候不需要像操作普通集合那样每进行一次求值操作就产生一个新的集合保存中间数据。
惰性求值 在编程语言理论中惰性求值表示一种在需要时才进行求值的计算方式。 在使用惰性求值的时候表达式不在它被绑定到变量之后就立即求值而是在该值被取用时才去求值。 通过这种方式不仅能得到性能上的提升还有一个重要的好处就是它可以构造出一个无限的数据类型。 序列的操作方式
list.asSequence().filter {it 2}.map {it * 2}.toList()序列总共执行了两类操作分别是:
filter{it 2}.map{it * 2}:filter和map的操作返回的都是序列我们将这类操作称为中间操作。toList():这一类操作将序列转换为List我们将这类操作称为末端操作。
Kotlin中序列的操作就分为
中间操作
中间操作都是采用惰性求值的例如:
list.asSequence().filter {println(filter($it))
}.map {println(map($it))
}上面操作中的println方法没有执行这说明filter和map方法的执行被延迟了正所谓惰性求值。惰性求值仅仅在该值被需要的时候才会真正去求值。 那么怎么去触发这个”被需要“的状态即——末端操作。
末端操作
末端操作就是一个返回结果的操作它的返回值不能是序列必须是一个明确的结果 比如列表、数字、对象等表意明确的结果。末端操作一般都放在链式操作的末尾 在执行末端操作的时候会去触发中间操作的延迟计算也就是将”被需要“这个状态被激活了
list.asSequence().filter {println(filter($it))it 2
}.map {println(map($it))it * 2
}.toList()
// 输出↓
filter(1)
filter(2)
filter(3)
map(3)
filter(4)
map(4)
filter(5)
map(5)
[6, 8, 10]可以看到所有的中间操作都被执行了。从上面执行打印的结果我们发现它的执行顺序与我们预想的不一样。
普通集合在进行链式操作的时候会先在list上调用filter然后产生一个结果列表接下来map就在这个结果列表上进行操作。而序列则不一样序列在执行链式操作的时候会将所有的操作都应用在一个元素也就是说第一个元素执行完所有的操作之后第二个元素再去执行所有的操作以此类推。
可null类型
因为在Kotlin中一切都是对象一切都是可null的。这就会防止出现NPE异常。当某个变量的值可以为null的时候必须在声明处的类型后添加?来标识该引用可为空。 Kotlin通过?将是否允许为空分割开来甚至Any也分可空和不可空。
var value1: String
value1 null // 编译错误 Null can not be a value of a non-null type String
var value2 : String?
value2 null // 编译通过在对可空变量进行操作时如果变量是可能为空的那么将不能直接调用否则将会报错。
安全调用操作符 ?.
当你对一个可空类型的变量进行操作时如果直接调用方法或访问属性可能会导致空指针异常那么此时可以使用 ?.
val length name?.length如果name不是nulllength将返回name的长度如果name是nulllength将直接返回null而不会抛出异常。
操作符 ?:
该操作符允许我们为可能为null的表达式提供一个默认值。
val length name?.length ?: 0在这个例子中如果name不为nulllength将返回其长度如果name为nulllength将返回0。
非空断言操作符 !!
当你确定一个变量绝对不为空时你可以使用!!操作符这将告诉编译器说我已经排除了null值的情况。但是如果该变量为null还是会抛出空指针异常。
val length name!!.length请谨慎使用!!操作符因为它会忽略Kotlin中对null安全性的所有保护措施。
使用类型检测及自动类型转换
is运算符检测一个表达式是否某类型的一个实例,如同Java的instanceof。 如果一个不可变的局部变量或属性已经判断出为某类型那么检测后的分支中可以直接当作该类型使用在大多数情况下is操作符会进行智能转换。表示编译器将变量当作与其声明的类型不同的类型而智能转换是说编译器替你自动地进行转换。 无需显式转换:
fun getStringLength(obj: Any): Int? {if (obj !is String) return null// obj 在这一分支自动转换为 String这是因为Kotlin的编译器帮我们做了转换// 这称为Kotlin中的智能转换(Smart Casts)。官方文档中这样介绍: 当且仅当Kotlin的编译器// 确定在类型检查后该变量不会再改变才会产生Smart Casts。return obj.length
}只要编译器能够保证在介于判断对象类型和被使用之间不能修改变量is操作符就会进行智能转换。
例如在上面的代码中编译器知道在介于调用is操作符和调用String的某个方法之间item变量不能被赋予另一类型的引用。但是在一些特殊情况下智能转换不会生效。例如is操作符不会对类中的var属性进行智能转换那是因为编译器无法保证该属性是否还会被更改。
安全的类型转换 as?
安全类型转换操作符as?尝试进行类型转换如果失败了不会抛出异常而是返回null。
val a: Int? 123.toIntOrNull()//将字符串123转换为整数类型由于转换成功所以intValue的值为123。
val b: Int? abc as? Int //将字符串abc尝试转换为整数类型由于转换失败所以intValueOrNull的值为null。
//a 123 b null返回和跳转
Kotlin有三种结构化跳转表达式和java差不太多:
return:默认从最直接包围它的函数或者匿名函数返回。break:终止最直接包围它的循环。continue:继续下一次最直接包围它的循环。 在Kotlin中任何表达式都可以用标签label来标记。标签的格式为标识符后跟符号例如:test 要为一个表达式加标签我们只要在其前加标签即可。
loop for (i in 1..100) {for (j in 1..100) {if (i j 100) breakloop//退出指定的循环 C语言的goto}
}