kotlin 之 杂谈

in关键字的使用

//如果存在于区间(1,Y-1),则打印OK  【判断区间】
if (x in 1..y-1) 
  print("OK")

//如果x不存在于array中,则输出Out  【用于判断】
if (x !in 0..array.lastIndex) 
  print("Out")

//打印1到5    【判断区间】
for (x in 1..5) 
  print(x)

//遍历集合(类似于Java中的for(String name : names))
for (name in names)
  println(name)

//如果names集合中包含text对象则打印yes  【判断包含】
if (text in names)
  print("yes")

when表达式


在Kotlin所有类中,有一个公共的superclass,名字就叫:Any。 所有没有显式声明基类的class,
它的默认父类就是***Any***

fun cases(obj: Any) { 
  when (obj) {
    1       -> print("第一项")
    "hello" -> print("这个是字符串hello")
    is Long -> print("这是一个Long类型数据")
    !is String -> print("这不是String类型的数据")
    else    -> print("else类似于Java中的default")
  }
}

is关键字
判断一个对象是否为一个类的实例,可以使用is关键字
与 Java 中的instanceof关键字类似,但在 Kotlin 中如果已经确定了一个对象的类型,可以在接下来的代码块中直接作为这个确定类型使用。

fun getStringLength(obj: Any): Int? {
  if (obj is String) {
    // 做过类型判断以后,obj会被系统自动转换为String类型
    return obj.length 
  }

  //同时还可以使用!is,来取反
  if (obj !is String){
  }

  // 代码块外部的obj仍然是Any类型的引用
  return null
}

**?. 与 ?: !! **
Kotlin 是空指针安全的,也就意味着你不会再看到那恼人的空指针异常。
例如这句代码 println(files?.size),只会在files不为空时执行。
?.就是当前面的变量!= nuil 时正常调用,如果为null就为null,!!就是当变量为null时,抛出空指针异常 (建议不要使用!! 使用就失去空安全的优点了)
?:操作符,elvis操作符,这个其实和可空类型没啥关系,这个也不是Java中的三目运算符,但是容易混淆

//当data不为空的时候,执行语句块
data?.let{
    //... 
}

//相反的,以下代码当data为空时才会执行
data?:let{
    //...
}

open 修饰符

Kotlin 默认会为每个变量和方法添加 final 修饰符。这么做的目的是为了程序运行的性能,其实在 Java 程序中,你也应该尽可能为每个类添加final 修饰符( 见 Effective Java 第四章 17 条)。
为每个类加了final也就是说,在 Kotlin 中默认每个类都是不可被继承的。如果你确定这个类是会被继承的,那么你需要给这个类添加 open 修饰符。

internal 修饰符
Java 有三种访问修饰符,public/private/protected,还有一个默认的包级别访问权限没有修饰符。
1、private
2、protected:一个包成员不能被定义为 protected.
3、internal:如果一个定义为 internal 的包成员的话,对所在的整个 module 可见。如果它是一个其他领域的成员,它就需要依赖那个领域的可见性。如有一个 private 类,那么它的 internal 修饰的函数的可见性会限制与它所在的该类的可见性。可以访问同一个 module 中的 internal 修饰的类,但不能访问其他 module的。
4、public: 仅受限于它的领域。一个定义为 public 的成员被包含在一个 privaet 修饰的勒种,这个成员在这个类之外也是不可见的。

枚举类
在 Kotlin 中,每个枚举常量都是一个对象。枚举常量用逗号分隔
其实在 Kotlin 中,枚举的本质是一个实现了Comparable的 class,其排序就是按照字段在枚举类中定义的顺序来的。

enum class Programer {
    JAVA, KOTLIN, C, CPP, ANDROID;
}

sealed 密封类
sealed 修饰的类称为密封类,用来表示受限的类层次结构。例如当一个值为有限集中的 类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。

data 数据类
data 修饰的类称之为数据类。它通常用在我们写的一些 POJO 类上。
当 data 修饰后,会自动将所有成员用operator声明,即为这些成员生成类似 Java 的 getter/setter 方法。

operator 操作符重载

Kotlin 有一些固定数量象征性的操作符,可以在任何类中很容易地使用。方法是创建一个方法,方法名为保留的操作符关键字,这样就可以让这个操作符的行为映射到这个方法。

inline (内联函数)
内联函数与普通的函数有点不同。一个内联函数会在编译的时候被替换掉,而不是真正的方法调用。这在译写情况下可以减少内存分配和运行时开销。例如,有一函数只接收一个函数作为它的参数。如果是普通函数,内部会创建一个含有那个函数的对象。而内联函数会把我们调用这个函数的地方替换掉,所以它不需要为此生成一个内部的对象。

 // 例一、创建代码块只提供 Lollipop 或更高版本来执行
inline fun supportsLollipop(code: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        code()
    }
}
// usage
supportsLollipop {
    window.setStatusBarColor(Color.BLACK)
}

lazy、lateinit 用于延迟初始化,第一次使用时再实例化
1、包含一个 lambda,当第一次执行 getValue 时该 lambda 会被调用,所以该属性可以被延迟初始化。之后的调用都只会返回同一个值。
2、lazy 操作符是线程安全的。
3、如果不担心多线程问题或想提高更多的性能,可以使用 lazy(LazyThreadSafeMode.NONE) { … }
4、一般 lazy 委托的代码块可以阻止在多个不同的线程中创建多个对象。
两者区别:
by lazy 修饰val的变量
lateinit 修饰var的变量,且变量是非空的类型

class App : Application() {
   val database: SQLiteOpenHelper by lazy {
       MyDatabaseHelper(applicationContext)
    }
    override fun onCreate() {
      super.onCreate()
      val db = database.writeableDatabase
    }
}

“by” 关键字
在扩展一个类并重写某些方法时,你的代码就变得依赖你自己继承的那个类的实现细节了。当你的基类的实现被修改或者新方法被添加进去,你做出的类行为的假设会失效,所以你的代码也许最后就会以不真确的行为告终。
因为有以上原因,kotlin的默认类都被修饰 final 不可更改的。
实现:by 委托,它的实现思想其实跟设计模式中的装饰器模式一样

// 定义一个接口,和一个方法 show()
interface Base {
fun show()
}
// 定义类实现 Base 接口, 并实现 show 方法
open class BaseImpl : Base {
override fun show() {
print("BaseImpl::show()")
}
}
// 将Base中所有的实现都委托给base 对象,这个类本身就不需要去实现Base接口中的方法
class BaseProxy(base: Base) : Base by base
// main 方法
fun main(args: Array<String>) {
val base = BaseImpl()
BaseProxy(base).show()
}
分析:每次在实现接口时,都需要实现接口中的所有方法,by也就是将 Base 接口中需要实现的
方法都委托给BaseImpl 类去实现,所以这里这样写 :Base by base 其实也就是说,我实现了接口
Base,但我本身不实现Base接口中的方法,委托给base帮我实现,那么在调用的时候就传入有具体实现了Base接口的类,方便我们的委托。
这里:
// 传入了BaseImpl类的引用,BaseImpl 必须是实现了Base接口的类
val base = BaseImpl()
// 传入了BaseImpl类的引用,这里在调用的时候也是调用BaseImpl中的方法
BaseProxy(base).show()
/* 在这里就把整个接口的实现委托给 持有 base 引用的类去实现接口Base中具体的方法
不代表Base接口在其他地方一定要有被其他实体类实现了才可以使用by 委托,即使在其他地方没
有实现Base 接口也可以使用 Base接口的对象去委托 */
class BaseProxy(base: Base) : Base by base

let
默认当前这个对象作为闭包的it参数,返回值是函数里面最后一行

fun testLet(){
       "Rocker".let {
           print(it)
           print(it)
       }
   }

apply

调用对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象

 fun testApply(){
        mutableListOf<String>().apply {
            add("jim")
            add("lili")
            add("Rocker")
        }.add("To test whether returns this object")
  }

Observable

1、该委托可以检测希望观察的属性变化。当被观察属性的 set 方法被调用时,它就会自动执行我们指定的 lambda 表达式。所以一旦该属性被赋予了新值,则可以收到被委托的属性、旧值和新值。

class ViewModel(val db: MyDatabase) {
  var myProperty by Delegates.observable("") {
    d,old,new ->
    db.saveChanges(this,new)
  }
}

Vetoable
一个特殊的 observable, 可以来决定是否保存这个值。在真正保存之前进行一些条件判断。

var positiveNumber = Delegates.vetoable(0) {
    d, old, new -> 
    new >= 0
}
// 上面这个委托只允许在新的值是正数时执行保存。在 lambda 中,最后一行表示返回值。不需要使用 return 关键字(实质上不能被编译)

Not Null
1、场景1:需要在某些地方初始化该属性,但不能在构造函数中确定,或不能在构造函数中做任何事。
2、场景2:在 Activity fragment service receivers…中,一个非抽象的属性在构造函数执行之前需要被赋值。
3、解决方案1:使用可 null 类型并且赋值为 null,直到真正去赋值。氮素,在使用时就需要不停的进行 not null 判断。
4、解决方案2:使用 notnull 委托。含有一个可 null 的变量并会在设置该属性时分配一个真实的值。如果该值在被获取之前没有被分配,它就会抛出一个异常。

class App : Application() {
  companion object {
    var instance: App by Delegates.notnull()
  }
   override fun onCreate() {
      super.onCreate()
      instance = this
    }
}

Companion

官方描述到这是 伴生 对象, companion 本身也是伴侣的意思. 外围内会持有 companion 对象的一个静态常年字段, 伴随着外围类 的加载而诞生
关于在 compain 中定义的变量的理解:

companion 中定义的变量通通都会在 外围类 定义成一个 私有 静态字段.
如果变量为 val , 那么 对应的 java 便是 static final.
变量为 private , 也就意味着 companion 中 不会为这个变量生成对应的访问方法,
我们可以看到, 其实 companion 就是个 空壳子, 字段不会位于 companion 类中, 只有方法会定义在 companion 内部.
companion 中的方法都不是静态方法, 其实我们访问任何 companion 中的字段都是通过 外围类持有的 companion 实例对象来访问的, 方法也是如此, 而且 companion 的构造方法也是 private 的

总结: companion 其实和 java 中的静态方法和字段很像, 只是我们访问这些字段和方法时需要通过 companion 实例对象来绕一下, 而外围类永远只会持有一个 companion 实例对象, 所以这和静态的结果没什么太大的区别.

从 Map 中映射值
另一种委托方式,属性的值会从一个map中获取 value,属性的名字对应这个map 中的 key。

import kotlin.properties.getValue
class Configuration(map: Map<String,Any?>) {
  val width: Int by map
  val height: Int by map
  val dp: Int by map
  val deviceName: String by map
}
// usage
conf = Configuration(mapof(
  "width" to 1080,
  "height" to 720,
  "dp" to 240,
   "deviceName" to "myDecive"
))

custom delegate
自定义委托需要实现 ReadOonlyProperty / ReadWriteProperty 两个类,具体取决于被委托的对象是 val 还是 var

// step1
private class NotNullSingleValueVar<T>() : ReadWriteProperty<Any?, T> {
  private var value: T? = null
  override fun getValue(thisRef: Any?, property: KProperty<*>): T {
      return value ?: throw IllegalStateException("${desc.name not initialized}")
  }
  
  override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
      this.value = if (this.value == null) value else throw IllegalStateException("${desc.name} already initialized")
      }
}
// step2: usage
object DelegatesExt {
  fun notNullSingleValue<T>(): ReadWriteProperty<Any?, T> = NotNullSingleValueVar()
}

集合和函数操作

1、Iterable: 父类。可以遍历一系列的都是实现这个接口
2、MutableIterable: 一个支持便利的同时可以执行删除的 Iterables
3、Collection:
4、MutableCollection:支持增加删除item 的 collection。提供了额外的函数,如 add、remove、clear等
5、List: 范性有序集合。
6、MutableList: 支持增删item 的 List
7、Set: 无序并不支持重复 item 的 集合
8、MutableSet: 支持增删item 的 Set
9、 Map:
10、MutableMap: 支持增删 item 的 map

总数操作符
1、Any:如果至少有一个元素符合给出的判断条件,则返回 true

val list = listOf(1,2,3,4,5,6)
assertTrue(list.any { it % 2 == 0 })
assertFalse(list.any { it > 10})

2、all:如果全部的元素符合给出的判断条件,则返回 true

assertTrue(list.all { it < 10})
assertFalse(list.all { it % 2 == 0})

3、count: 返回符合给出判断条件的元素总数

assertEquals(3,list.count {it % 2 == 0})

4、fold: 在一个初始值的基础上从第一项到最后一项通过一个函数累计所有的元素

asserEquals(25, list.fold(4) { total, next -> total + next})

5、foldRight: 与 fold 一样,但顺序是从最后一项到第一项。

assertEquals(25, list.foldRight(4) { total, next -> total + next
})

6、forEach: 遍历所有元素,并执行给定的操作。

list.forEach { println(it) }

7、forEachIndexed: 与 forEach ,同时可得到元素的 Index

list.forEachIndexed { index, value -> println("position $index contains a $value")}

8、max: 返回最大一项,如果没有则返回 null

assertEquals(6, list.max())

9、maxBy: 根据给定的函数返回最大的一项,没有返回 null

assertEquals(1, list.maxBy { -it })

10、min

11、minBy

assertEquals(1, list.maxBy { -it })// The element whose negative is greater

12、none

assertTrue(list.none { it % 7 == 0 })// No elements are divisible by 7

13、reduce:与fold一样,但没有初始值。通过一个函数从第一项到最后一项进行累计。

assertEquals(21, list.reduce {total, next -> total + next})

14、reduceRight: 顺序从最后一项到第一项

assertEquals(21, list.reduceRight { total, next -> total + next
})

15、sumBy: 返回所有每一项通过函数转换之后的数据的总和。

assertEquals(3, list.sumBy { it % 2 })

过滤操作符

1、drop:返回包含去掉前 n 个元素的所有元素的列表

listOf(5,6), list.drop(4)

2、dropWhile: 返回根据给定函数从第一项开始去掉指定元素的列表
assertEquals(listOf(3, 4, 5, 6), list.dropWhile { it < 3 })
3、dropLastWhile:返回根据给定函数从最后一项开始去掉指定元素的列表
4、filter:过滤

listOf(2,4,6), list.filter{it % 2 == 0}

5、filterNot
6、filterNotNull
7、slice:过滤一个list 中指定 index 的元素
8、take: 返回从第一个开始的 n 个元素

9、takeLast: 返回从最后一个开始的 n 个元素

assertEquals(listOf(1, 2), list.take(2))
10、takeWhile: 返回从第一个开始符合给定函数条件的元素

映射操作符
1、flatMap:遍历所有的元素,为每一个创建一个集合,最后把所有集合放在一个集合中。

map:遍历每一个元素flatMap:遍历每一个元素,并铺平元素
var list =listOf(listOf(10,20),listOf(30,40),listOf(50,60))
var mapList = list.map{element->element.toString()}
var flatMapList = list.flatMap{element->element.asIterable()}
flatMap中的函数一定要返回一个Iterable,不然报错
结果:[[10, 20], [30, 40], [50, 60]][10, 20, 30, 40, 50, 60]

2、groupBy:返回一个根据给定函数分组后的 map
3、map:返回一个每一个元素根据给定函数转换所组成的list
4、mapIndexed:返回一个每一个元素根据给定的包含元素 index 的函数转换所组成的 list
5、mapNotNull

生产操作符

1、merge:把两个集合合并为一个新的,相同index的元素通过给定的函数进行合并生成新的元素作为新集合中的一个元素,返回新集合。新集合的大小由最小的那个集合大小决定

val list = listOf(1, 2, 3, 4, 5, 6)
val listRepeated = listOf(2, 2, 3, 4, 5, 5, 6)
assertEquals(listOf(3, 4, 6, 8, 10, 11), list.merge(listRepeated
) { it1, it2 -> it1 + it2 })

2、partition: 把一个给定集合的分割为两个,第一个集合是由原集合每一项元素匹配给定函数条件返回 true 的元素组成,第二集合为false

assertEquals(
    Pair(listOf(2, 4, 6), listOf(1, 3, 5)) , list.partition { it % 2 == 0 } 
)

3、plus

assertEquals(
    listOf(1, 2, 3, 4, 5, 6, 7, 8) , list + listOf(7, 8)
)

4、zip:返回由 pair 组成的 list,每个 pair 由 两个集合中相同index 的元素组成。该返回的 list 大小由最小的那个集合决定。

assertEquals(
    listOf(Pair(1, 7), Pair(2, 8)) , list.zip(listOf(7, 8))
)

5、unzip:从包含 pair 的 List 中生成包含List的Pair

assertEquals(
    Pair(listOf(5, 6), listOf(7, 8)) , listOf(Pair(5, 7), Pair(6, 8)).unzip()
)

顺序操作符
1、reverse:返回一个与指定list相反顺序的list

val unsortedList = listOf(3, 2, 7, 5)
assertEquals(listOf(5, 7, 2, 3), unsortedList.reverse())

2、sort:返回一个自然排序后的list

assertEquals(listOf(2, 3, 5, 7), unsortedList.sort())

3、sortBy:指定函数排序

assertEquals(listOf(3, 7, 2, 5), unsortedList.sortBy { it % 3 })

4、sortDescending:降序

assertEquals(listOf(7, 5, 3, 2), unsortedList.sortDescending())

5、sortDescendingBy:指定函数降序

assertEquals(
  listOf(2, 5, 7, 3),
  unsortedList.sortDescendingBy {
    it % 3 
})

嵌套类

//在Java中,可以在类的里面再定义类。如果它是一个通常的类,它不能去访问外部类的成员(就如Java中的static):

class Outer {
  private val bar: Int = 1
  class Nested {
        fun foo() = bar//编译不通过
  }
}

内部类 inner 修饰
//如果需要去访问外部类的成员,我们需要用 inner 声明这个类:

class Outer {
  private val bar: Int = 1
  inner class Inner{
        fun foo() = bar//没问题
  }
}
val demo = Outer().Inner().foo() // == 1

dynamic

1.dynamic 类型关闭了 kotlin 的类型检查:
val dyn: dynamic = …
2.dynamic 最奇特的特性就是可以在 dynamic 变量上调用任何属性或任何方法
dyn.whatever(1, “foo”, dyn)
dyn.whatever(*array(1, 2, 3))

猜你喜欢

转载自blog.csdn.net/weixin_41620505/article/details/90665123