Kotlin笔记

要点1:主构造器

class User constructor(var id:Int, var name:String?, var job:String?) {
  init{
    // 初始化代码块
  }
  // 次构造器必须(可间接)调用主构造器
  constructor() : this(null, null, null)
}

要点2:data class [语法糖]

  • 编译器会自动生成:equals、hashCode、toString、copy函数
data class User constructor(var id:Int, var name:String?, var job:String?) 
  • Copy函数:
val user = User(1, "Name","Engineer")
val userCopy = user.copy()
println(user == userCopy)  // true, '=='比较对象
println(user === userCopy)  // false, '==='比较地址
  • 解构:可以不用返回包装类,直接返回所有类属性
fun getUserInfo() = User(1, "Name","Engineer")
fun main() {
    // 写法:
    val(id, name, job) = execute()
    println("id=$id, name=$name, job=$job")
}

原理:data class自动生成component1,2,3...方法对应构造器参数

@Nullable
public final String component1() { return this.id; }

@Nullable
public final String component2() { return this.name; }

@Nullable
public final String component3() { return this.job; }

要点3:非空判断

// kotlin语法
var job = user.job ?: "自由职业"
// 等价于 
var job = user.job
if (job == null) {
  job = "自由职业"
}

?. 联用

// kotlin写法
if (user.name?.length ?:0 < 6) {
}
// 等价于
if (user.name == null || user.name!!.length < 6) {
}

要点4:利用filter和forEach来替代for循环及if条件判断

// 原写法
for(user in users) {
  if(user.job == "Engineer") {
    selectedUsers.add(user)
    println(user.name)
  }
}
// 使用forEach简化后
users.forEach { //it代指参数
  if (it.job == "Engineer") {
    selectedUsers.add(user)
    println(user.name)
  }
}
// 使用filter简化
users.filter { it.job == "Engineer" }.forEach { println(it.name) }
  • 写法说明:函数如果最后一个参数是lambda,则可以将lambda写在()外,如果仅有lambda一个参数,则()也可以省略。

要点5:循环-Kotlin

// repeat函数,循环n次
repeat(50) {
  println(it)
}
// for-i循环,使用区间
val array = intArrayOf(1,2,2,3,4,5,6,7,8,9,50) 
for (i in 0 until array.size) {
}
for (i in array.indices) {
}

要点6:函数嵌套-kotlin

  • Kotlin允许函数内嵌套函数,内部函数可以访问外部函数的属性
  • 优点:如该内部函数仅有此调用,则可以增强代码可读性,避免被其他人调用
  • 缺点:嵌套函数会在每次被调用时生成额外对象,在循环中则不建议使用嵌套函数,避免生成过多垃圾对象
private fun outerFun() {
  val verifyCode = code.text.toString()
  ...
  fun verify() : Boolean {
    if (verifyCode?.length?:0<6) {
      Logger.e("验证码不正确")
      return false
    }
  }
}

要点7:对象自定义get方法名,禁用set

  • Kotlin会为非private的var对象创建get()/set()方法;
  • 如果不想让set方法暴露,可使用private set修饰
  • 如果想自定义get方法名,可使用注解@get:JvmName("selfieGetApp")
companion object {
  @get:JvmName("selfieGetApp")
  @JvmStatic
  lateinit var currentApp: Context
    private set
}

要点8:类型推断

  • 可简化函数声明
// 原函数声明
fun saveCache(key:String?, value:String?) : Unit { 
  cacheMap.putString(key, value)
}
fun getCache(key:String?) : String? { 
  return cacheMap.getString(key, null)
}
// 类型推断用上,简化后
fun saveCache(key:String?, value:String?) = cacheMap.putString(key, value)
fun getCache(key:String?) = cacheMap.getString(key, null)

要点9:函数参数默认值

  • 可简化重复代码,避免函数重载
  • 如在Java中调用,则通过注解@JvmOverloads,生成对应的重载函数
// 函数定义
@JvmOverloads
fun toast(string:String?, duration:Int=Toast.LENGTH_SHORT) {
  Toast.makeText(BaseApplication.currentApplication, string, duration).show()
}

// 函数调用
toast("Hello World")
toast("Hello World", 3000)

要点10:扩展 (kotlin非常好用的一个特性)

  • 可给任何一个类加上成员属性或成员函数
  • 可用来替换工具类
  • 如果把已存在的函数或属性用扩展覆盖了怎么办?
    答:以原有成员函数为准
  • 如果为父子类均扩展了相同函数怎么办?
    答:编译器生成拓展函数时,分别以父/子类的类型作为第一个函数参数,所以调用域是哪个就会调用到哪个的拓展函数
// 定义:给Float类扩展dp2px函数
fun Float.dp2px() : Float {
  // 使用this访问自身
  return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, displayMetrics)
}

// 调用
12f.dp2px()

再来一个添加扩展成员属性的例子

// 取LinearLayout第一个子元素
val LinearLayout.firstChild: View
    get() = getChildAt(0) 

要点11:内联函数(inline关键字)

详见 Kotlin内联函数使用场景

要点12:函数类型 作为 参数/返回值

  • Kotlin中允许函数作为函数的参数或返回值,从而可替代Java中接口类包装
  • 实际上,函数类型参数 是kotlin在Functions.kt中预定义好的一系列接口,从Function0..22
// 定义一个函数
fun setOnClickListener(listener : (View) -> Unit) {
}

val view = View(context)
// 调用1:  ‘::’双冒号传递函数对象
view.setOnClickListener(::onClick)
fun onClick(view : View) {
  println("点击")
}
//调用2:匿名函数
view.setOnClickListener(fun (view:View) {
  println("点击")
})
//调用3:Lambda形式
view.setOnClickListener { //使用it代指view
  println("点击")
}

要点13:抽象属性

Kotlin支持在接口声明抽象属性

interface BaseView<T> {
    val presenter:T
}
// 实现处
val userPresenter = UserPresenter(this)
override val presenter: UserPresenter?
  get() = userPresenter 

要点14:kotlin特性-委托

  • by lazy:
    作用1:by lazy包含的代码只会加载一次
    作用2:by lazy修饰的对象仅在被调用时才创建
    于是,要点13中的代码就可以优化为:
override val presenter: LessonPresenter by lazy {
  UserPresenter(this)
}
  • 通过by关键词实现属性委托:

先看一段不优雅的代码

var userid: String
  set(value) {
     CacheUtils.save("userid", value)
  }
  get() {
     return CacheUtils.get("userid")!!
  }

var username: String
  set(value) {
     CacheUtils.save("username", value)
  }
  get() {
     return CacheUtils.get("username")!!
  }

使用委托优化
1、声明一个Saver类,构造函数传入key,实现getValue/setValue方法执行原本的set/get操作

class Saver(var key: String) {
  operator fun getValue(activity: MainActivity, property: KProperty<*>): String {
    return CacheUtils.get(key)!!
  }
  operator fun setValue(activity: MainActivity, property: KProperty<*>, value: String) {
    CacheUtils.save(key, value)
  }
}

2、在声明userid、username变量时,将对应的get/set操作委托给Saver对象

var userid: String by Saver("userid")
var username: String by Saver("username")

通过反编译看看编译器是如何实现的呢?

// 创建Saver对象 xxx$delegate 
@NotNull
private final MainActivity.Saver userid$delegate = new MainActivity.Saver("userid");
@NotNull
private final MainActivity.Saver username$delegate = new MainActivity.Saver("username");

// 在get/set方法里,通过分别创建的Saver对象执行getValue/setValue方法
@NotNull
public final String getUserid() {
    return this.userid$delegate.getValue(this, $$delegatedProperties[0]);
}
public final void setUserid(@NotNull String var1) {
    this.userid$delegate.setValue(this, $$delegatedProperties[0], var1);
}

@NotNull
public final String getUsername() {
    return this.username$delegate.getValue(this, $$delegatedProperties[1]);
}
public final void setUsername(@NotNull String var1) {
    this.username$delegate.setValue(this, $$delegatedProperties[1], var1);
}

要点15:kotlin内置标准函数(作用域函数)

详见 kotlin作用域函数let/with/run/apply/also

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容