kotlin 之 杂谈上

简介: kotlin 之 杂谈上

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


目录
相关文章
|
Java 开发工具 Android开发
Kotlin入门学习
Kotlin入门学习
68 0
|
17天前
|
存储 Java 编译器
Kotlin学习教程(八)
Kotlin学习教程(八)
|
18天前
|
安全 Java 编译器
Kotlin学习教程(一)
在5月18日谷歌在I/O开发者大会上宣布,将Kotlin语言作为安卓开发的一级编程语言。并且会在Android Studio 3.0版本全面支持Kotlin。 Kotlin是一个基于JVM的新的编程语言,由JetBrains开发。JetBrains作为目前广受欢迎的 Java IDE IntelliJ的提供商,在Apache许可下已经开源其Kotlin编程语言。 Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。 Kotlin已正式成为Android官方开发语言。
42 3
Kotlin学习教程(一)
|
18天前
|
Java Kotlin
Kotlin学习教程(二)
上一篇文章介绍了`Kotlin`的基本语法,我感觉在继续学习更多知识之前有必要单独介绍以下编码规范。 不管学什么东西,开始形成的习惯以后想改都比较困难。所以开始就用规范的方式学习是最好的。
41 0
|
3月前
|
安全 Java Android开发
探索Android应用开发中的Kotlin语言
【7月更文挑战第19天】在移动应用开发的浩瀚宇宙中,Kotlin这颗新星以其简洁、安全与现代化的特性,正迅速在Android开发者之间获得青睐。从基本的语法结构到高级的编程技巧,本文将引导读者穿梭于Kotlin的世界,揭示其如何优化Android应用的开发流程并提升代码的可读性与维护性。我们将一起探究Kotlin的核心概念,包括它的数据类型、类和接口、可见性修饰符以及高阶函数等特性,并了解这些特性是如何在实际项目中得以应用的。无论你是刚入门的新手还是寻求进阶的开发者,这篇文章都将为你提供有价值的见解和实践指导。
|
5月前
|
Android开发 Kotlin
android开发,使用kotlin学习Lifecycles
android开发,使用kotlin学习Lifecycles
81 0
|
5月前
|
Android开发 Kotlin
android开发,使用kotlin学习WorkManager
android开发,使用kotlin学习WorkManager
96 0
|
XML 存储 算法
Kotlin 实战 | 时隔一年,用 Kotlin 重构一个自定义控件
一年前,用 Java 写了一个高可扩展选择按钮库。只用单个控件实现单选、多选、菜单选,且选择模式可动态扩展。 一年后,试着用 Kotlin 重写该控件。
789 0
|
Java Kotlin
kotlin 之 杂谈下
kotlin 之 杂谈下
88 0
|
安全 Java 编译器
Kotlin刨根问底(一):你真的了解Kotlin中的空安全吗?(下)
行文结构:要点提炼(方便回顾) + 常规操作 + 源码层面摸索实现原理, 内容部分摘取自:《Kotlin实用指南》
132 0