要点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关键字)
要点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);
}