读官方文档做的一些笔记
1. 字符串
使用三个引号("""
)分界符括起来,内部没有转义并且可以包含换行以及任何其他字符
字符串模板
模板表达式以$开头,求出它后面的值
val s = "abc"
println("$s.length is ${s.length}") // 输出“abc.length is 3”
${'$'} //使用$本身
2. 类型的自动转化
如果一个不可变的局部变量或属性已经判断出为某类型,那么检测后的分支中可以直接当作该类型使用,无需显式转换
fun getStringLength(obj: Any): Int? {
// `obj` 在 `&&` 右边自动转换成 `String` 类型
if (obj is String && obj.length > 0) { #不是 !is
return obj.length
}
return null
}
3. if,for,while,when,in
// 1. if
fun maxOf(a: Int, b: Int) = if (a > b) a else b
fun foo(param: Int) {
val result = if (param == 1) {
"one"
} else if (param == 2) {
"two"
} else {
"three"
}
}
// 2. for
val items = listOf("apple", "banana", "kiwifruit")
for (item in items) {
println(item)
}
for (index in items.indices) { //有索引
println("item at $index is ${items[index]}")
}
for ((index, value) in array.withIndex()) { //索引和值
println("the element at $index is $value")
}
// 3. while
var index = 0
while (index < items.size) {
println("item at $index is ${items[index]}")
index++
}
// 4. when 必须有else 多个用逗号隔开
fun describe(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long"
!is String -> "Not a string"
in 'a'..'z', in 'A'..'Z' -> "It's a letter!"
else -> "Unknown"
}
//可以将 when 所判断的表达式捕获到变量中
fun Request.getBody() =
when (val response = executeRequest()) {
is Success -> response.body
is HttpError -> throw HttpException(response.status)
}
// 5. in
for (x in 1..10 step 2) {
print(x)
}
for (x in 9 downTo 0 step 3) {
print(x)
}
4. 遍历 map/pair型list
for ((k, v) in map) {
println("$k -> $v")
}
5. 区间
for (i in 1..100) { …… } // 闭区间:包含 100
for (i in 1 until 100) { …… } // 半开区间:不包含 100
for (x in 2..10 step 2) { …… }
for (x in 10 downTo 1) { …… }
if (x in 1..10) { …… }
6. list和map
val list = listOf("a", "b", "c")
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
// 过滤list
val positives = list.filter { x -> x > 0 }
val positives = list.filter { it > 0 }
7. 扩展函数
fun String.spaceToCamelCase() { …… }
"Convert this to camelcase".spaceToCamelCase()
8. 与null相关
Kotlin将变量分为可以为Nullable类型 Non-Null类型,变量在声明时就要确定属于哪个阵营。变量默认Non-Null类型,如果想声明Nullable的变量,需要用“?”修饰 var str: String? = "Hello"
- Nullable变量无法直接赋值给Non-Null变量,如果要赋值的话可以先判断是不是null,也可以用!!
var a: String? = "hello"
var b = "world"
if (a != null) {
b == a
}
var a: String? = "hello"
var b = "world"
b = a!! //使用“!!”方法要注意,当a为null时会抛出KotlinNullPointerException异常。
Nullable变量进行操作时要带“?”,当变量为null时,不会出现异常,而是返回结果null
- 与null相关的习惯写法
Nullable变量进行操作时要带“?”,当变量为null时,不会出现异常,而是返回结果null:
var name: String? = null
var len = name?.length
print(len == null) //输出:true
val files = File("Test").listFiles()
println(files?.size ?: "empty") //if not null and else 缩写
// 在可能会空的集合中取第一元素
val emails = …… // 可能会是空集合
val mainEmail = emails.firstOrNull() ?: ""
// if not null 执行代码
val value = ……
value?.let {
…… // 代码会执行到此处, 假如data不为null
}
// 如果该值或其转换结果为空,那么返回 defaultValue
val mapped = value?.let { transformValue(it) } ?: defaultValue
//可空布尔
val b: Boolean? = ……
if (b == true) {
……
} else {
// `b` 是 false 或者 null
}
9. apply(对于配置未出现在对象构造函数中的属性非常有用)
val myRectangle = Rectangle().apply {
length = 4
breadth = 5
color = 0xFAFAFA
}
10. 交换两个变量
var a = 1
var b = 2
a = b.also { b = a }
11. Kotlin 中的数字没有隐式拓宽转换,所以赋值比较都不可以。例如,具有 Double
参数的函数只能对 Double
值调用,而不能对 Float
、 Int
或者其他数字值调用。显示转换是toXxxx()
数字里可以使用下划线让其更加易读
12. ===是引用相等, ==是值相等(equals)
13. 数组
arrayOf(1, 2, 3)
创建了 array [1, 2, 3]
arrayOfNulls()
可以用于创建一个指定大小的、所有元素都为空的数组
用接受数组大小以及一个函数参数的 Array
构造函数,用作参数的函数能够返回给定索引的每个元素初始值:
// 创建一个 Array<String> 初始化为 ["0", "1", "4", "9", "16"]
val asc = Array(5) { i -> (i * i).toString() }
asc.forEach { println(it) }
14. 尾递归优化
就是递归时只返回函数本身,没有其他计算,写成这样的递归在编译时会被自动优化,或者运行时会清除上次的栈信息,因为上次执行中的任何数据在下次执行时都是无关的
tailrec fun add(num:Int, result:Int):Int{
if(num == 1){
return 1
} else {
return add(num - 1, result + num)
}
}
5. open
类要被继承,函数要被重写都要在前面加上open,重写要在函数前面加上override
16. 接口和类
abstract class Human{ //本质
abstract fun eat()
}
interface Code{ //能力
fun code()
}
class Man:Human(), Car{
override fun code(){
}
override fun eat(){
}
}
18. sealed class
19. 泛型
协变(covariant):生产者,可以读数据但不能写数据,因为我们不知道什么对象符合那个未知的 E
的子类型。 E是一个协变类型的话只能作为返回类型,不能用作参数类型。Java里用<? extends E>,不可以add
逆变(controvariant):消费者,可以写数据但不能存数据。 Java里用<? super E>,不可以get
fun main(){
var num: Number = 123
var myClass = MyClass("abc", num)
myTest(myClass)
}
class MyClass<out T, in M>(t: T, m: M){
private var t: T = t
private var m: M = m
fun get(): T = this.t
fun set(m: M) = {
this.m = m
}
}
fun myTest(myClass: MyClass<String, Number>){
var myObject: MyClass<Any, Int> = myClass //加上了 in 和 out 就可以引用
}
声名处协变,out出现在类声明的时候
如果泛型类只是将泛型类型作为其输入类型,那么我们就可以使用out。
如果泛型类只是将泛型类型作为其返回类型,那么我们就可以使用in。
对于 out 泛型来说,我们可以将子类型对象赋给父对象引用。对于 out 泛型,在取数据 get 的时候,子类型对象作为返回值可以传给符类型的返回值。
对于 in 泛型来说,我们可以将父类型对象赋给子对象引用。对于 in 泛型,在存数据 add 的时候,子类型对象作为参数可以传给父类型参数。
interface Producer<out T>{
fun produce(): T
}
interface Consumer<in T>{
fun consumer(item: T)
}
open class Fruit
open class Apple: Fruit()
class ApplePear: Apple()
//---------------------------------------
class FruitProducer: Producer<Fruit> {
override fun producer(): Fruit{
return Fruit()
}
}
class AppleProducer: Producer<Apple> {
override fun producer(): Apple{
return Apple()
}
}
class ApplePearProducer: Producer<ApplePear> {
override fun producer(): ApplePear{
return ApplePear()
}
}
//----------------------------------------
class FruitConsumer: Consumer<Fruit>{
override fun consume(item: Fruit){
println()
}
}
class AppleConsumer: Consumer<Apple>{
override fun consume(item: Apple){
println()
}
}
class ApplePearConsumer: ApplePearConsumer<ApplePear>{
override fun consume(item: ApplePear){
println()
}
}
fun main(){
val producer1: Producer<Fruit> = FruitProducer()
val producer2: Producer<Fruit> = AppleProducer()
val producer3: Producer<Fruit> = ApplePearProducer()
val consumer1: Consumer<ApplePear> = ApplePearConsumer()
val consumer2: Consumer<ApplePear> = AppleConsumer()
val consumer3: Consumer<ApplePear> = FruitConsumer()
}
类型投影(使用处协变),因为out只能作为返回类型,in只能作为参数类型很不方便。出现在定义方法的时候
fun copy(from: Array<out Any>, to: Array<Any>){
for(i in from.indices){
to[i] = from[i]
}
}
fun main(){
val from: Array<Int> = arrayOf(1, 2, 3, 4)
val to: Array<Any> = Array(4, {_ -> "hello"})
copy(from, to)
}
星投影
Star<out T> 如果T的上界是TUpper,那么Star<*>就相当于Star<out T>,这表示当T的类型未知时,可以从Star<*>当中安全地读取任何值
Star<in T> Star<*>就相当于Star<in Nothing>,这表示无法向其中写入任何值
Star<T> 如果T的上界是TUpper,那么Star<*>就相当于读取时的Star<out TUpper>,写入时的Star<in Nothing>(可以读,不能写)
class Star1<out T>{
}
class Star2<in T>{
fun setValue(t: T){
}
}
class Star3<T>(private var t: T){
fun setValue(t: T){
}
fun getValue(): T{
return this.t
}
}
fun main(){
val star1: Star1<Number> = Star1<Int>()
val star2: Star1<*> = star1
val star3: Star2<Int> = Star2<Number>
val star4: Star2<*> = star3
//star.setValue(3) compile error
val star5: Star3<String> = Star3<String>("hello")
val star6: Star3<*> = star5
}
UpperBoundsClass<T: List<T>> 一个上界
UpperBoundsClass<T> where T: Comparable<T>, T: Any 两个上界
20. 可变参数可以借助于分散运算符(*)以具名函数的形式传递,可变参数通常为最后一个参数
test(strings = *arrayOf("a", "b", "c"))
fun test(vararg strings: String){
strings.forEach(println(it))
}