文章目录






一.Kotlin属性

1.默认方法
var uuid:String
get() = CacheTool.localCache.getString(UUID)?:""
set(value) {
CacheTool.localCache.put(UUID,value)
}
2.get 和set属性

我们可以为属性定义自定义的访问器。如果我们定义了一个自定义的 getter,那么每次访问该属性时都会调用它 (这让我们可以实现计算出的属性)。以下是一个自定义 getter 的示例:

val isEmpty: Boolean
get() = this.size == 0

如果我们定义了一个自定义的 setter,那么每次给属性赋值时都会调用它。一个自定义的 setter 如下所示:

var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // 解析字符串并赋值给其他属性
}

按照惯例,setter 参数的名称是 ​​value​​,但是如果你喜欢你可以选择一个不同的名称。

自 Kotlin 1.1 起,如果可以从 getter 推断出属性类型,则可以省略它:

val isEmpty get() = this.size == 0  // 具有类型 Boolean

如果你需要改变一个访问器的可见性或者对其注解,但是不需要改变默认的实现, 你可以定义访问器而不定义其实现:

var setterVisibility: String = "abc"
private set // 此 setter 是私有的并且有默认实现

var setterWithAnnotation: Any? = null
@Inject set // 用 Inject 注解此 setter

如不想对外公开某个方法时,可以使用修饰符​​private​​实现,如:

// 是否加载失败
var isLoadFail = false
private set // 此 setter 是私有的并且有默认实现

设置 set 属性为private 后, 那么它将只在同一个源代码文件内可以访问,在其他作用域下 如其他包名下不能访问。

注意:这种方式只适用于 set 方法, get的访问权限默认和属性是一致的,如下面的使用会编译错误

var isLoadFail = false
private get // 编译错误,get的访问权限和属性一致
3.get /set 方法使用注意事项
var isSelected: Boolean = false
set(value) {
isSelected = value
invalidate()
}

这段代码会使得循环调用,最终ANR,正确的写法应该是:

var isSelected: Boolean = false
set(value) {
field = value
invalidate()
}

二.基本类型

Double类型值 如:123.5, 123.5e10

Float值需要用f或F标识:123.5f

Long类型需要大写的L来标识:123L

在数字字面值中添加下划线,提高可读性。(kotlin1.1开始支持)

val oneMillion = 1_000_000
val creditCardNumber = 1234_5678L

三.when表达式

如果对多种条件需要进行相同的处理,那么可以对一个分支指定多个条件,有逗号分隔:

when (x){
0,1 -> print("x==0 or x==1")
else -> print("otherwise")
}

四.var、val、lateinit、by lazy的使用

var 是一个变量,初始化的值可以修改,val 是一个常量,只读。

lateinit 和lazy是两种不同的延迟初始化

lateinit 只用于变量var ,lazy只用于常量val

lateinit 不能用于可空的属性上和java的基本类型

private val money: Int by lazy {1} //正确
private lateinit val test:String //正确

lateinit val test:String //error
lateinit val test:Float //error


lazy 应用于单例模式 (if-null-then-init-else-return),而且当且仅当常量被第一次调用的时候,委托方法才会被执行。


实际项目中运用举例:

1.比如这样的常见操作,只获取,不赋值(val类型),并且多次使用的对象

private val mUserManager : UserManager by lazy {
UserManager.getInstance()
}

2.再比如 Activity中控件的初始化,一般传统的进入界面就初始化所有的控件,而使用懒加载,只有用到时才会对控件初始化

//kotlin 封装:
fun <V : View> Activity.bindView(id: Int): Lazy<V> = lazy {
viewFinder(id) as V
}

//acitivity中扩展调用
private val Activity.viewFinder: Activity.(Int) -> View?
get() = { findViewById(it) }

//在activity中的使用姿势
val mTextView by bindView<TextView>(R.id.text_view)
mTextView.text="执行到我时,才会进行控件初始化"


lateinit则用于只能在生命周期流程中进行获取或初始化的变量。有一些在 Android 中某些属性需要在 onCreate() 方法中初始化。


private lateinit var mAdapter: RecyclerAdapter<Transaction>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAdapter = RecyclerAdapter(R.layout.item_transaction)
}

//或者类似这样的
private lateinit var vehicleListDialog: VehicleListDialog

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initVehicleListDialog()
}
private fun initVehicleListDialog() {
var dialog = childFragmentManager.findFragmentByTag(VehicleListDialog.TAG) as VehicleListDialog?
if (dialog == null) {
dialog = VehicleListDialog()
}
//进行初始化
vehicleListDialog = dialog
}

再比如:

class App:Application(){
init {
instance = this
}

@Inject lateinit var apiComponent: ApiComponent
override fun onCreate() {
super.onCreate()
DaggerApiComponent.builder().apiModule(ApiModule()).appModule(AppModule(this)).build().inject(this)

companion objectf{
lateinit var instance:App
}
}

注意:要慎用,使用延迟初始化时 要注意,变量的使用前需要初始化,否则会报错。

如:使用对象判断是否初始化

private lateinit var avatarFile: File
//是否初始化
private fun isAvaFileInitialzed():Boolean = ::avatarFile.isInitialized

五. !!.和?.的区别

!!.和?.都是Kotlin提供的检测空指针的方法。

“?”用来明确指定一个对象,或者一个属性变量是否可以为空。

private var mContext:Context? = null

"?"加在变量名后,系统在任何情况不会报它的空指针异常。

"!!"加在变量名后,如果对象为null,那么系统一定会报异常!

?.
//kotlin
foo?.run()

//与java相同
if(foo != null){
foo.run()
}//不会空指针

!!.
//kotlin
foo!!.run()

//与java相同
if(foo != null){
foo.run();
}else{//为空,则出现空指针
throw new KotlinNullPointException();
}

六.Anko用于执行后台任务

搭配 ​​Anko​​ lib 使用。后台和主线程的切换特别直观和简单。uiThread 在主线程上运行,并且我们不需要关心 Activity 的生命周期(pause 与 stop), 所以也不会出错了。

如果一个Activity调用doAsync,那么如果该Activity消亡(isFinishing返回true)uiThread代码是不会执行的。这样,我们就避免了AsyncTask经常出现的错误或其他没有注意activity生命周期的任何回调函数。

doAsync{
//后台执行
var result = getResult()
//回到主线程
uiThread{
toast(result)
}
}

项目中运用实例:

如在微信支付中,接收支付成功或失败的回调事件。

doAsync {
WeChatPay.wxApi.handleIntent(intent, object : IWXAPIEventHandler {
override fun onReq(req: BaseReq) {}

override fun onResp(resp: BaseResp) {
uiThread {
if (resp.type == ConstantsAPI.COMMAND_PAY_BY_WX) {
handlePayResponse(resp)
}
finish()
}
}
})
}

七.with 、apply、let、run、takeif等函数的使用场景总结

1.with

定义:

fun<T,R>with(receiver:T,block:T.() ->R):R


功能:将对象作为函数的参数,在函数内可以通过this指代该对象。返回值为函数的最后一行或return 表达式。


实例1:

/**
* 画笔对象的引用
*/
private var paint = Paint()
paint.strokeCap = Paint.Cap.ROUND
paint.isAntiAlias = true // 消除锯齿
paint.isDither = true //防止抖动
paint.color = roundColor // 设置圆环的颜色
paint.style = Paint.Style.FILL // 设置空心
paint.strokeWidth = roundWidth // 设置圆环的宽度

//用with后更自然
private var paint = Paint()
with(paint) {
//this指代Paint对象,此处可以省略
strokeCap = Paint.Cap.ROUND
isAntiAlias = true // 消除锯齿
isDither = true //防止抖动
color = roundColor // 设置圆环的颜色
style = Paint.Style.FILL // 设置空心
strokeWidth = roundWidth // 设置圆环的宽度
}

实例2:

var list = mutableListOf<Int>()
list.add(1)
list.add(2)

//用with后
var list = with(mutableListOf<Int>()){
add(1)
add(2)
this
}
2.apply

定义:

fun T.apply(block:T.() ->Uint):T


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


实例:

var list = mutableListOf<Int>().apply{
add(1)
add(2)
}


val paint = Paint().apply {
strokeCap = Paint.Cap.ROUND
isAntiAlias = true // 消除锯齿
isDither = true //防止抖动
color = roundColor // 设置圆环的颜色
style = Paint.Style.FILL // 设置空心
strokeWidth = roundWidth // 设置圆环的宽度
}

此外由于apply函数返回的是其对象本身,那么可以配合?.完成多级的非空判断操作,或者用于建造者模式的Builder中。

3.let

定义:

fun <T,R> T.let(block:(T) -> R):R


功能:调用对象(T)的​​let​​函数,则该对象为函数的参数。在函数内可以通过 it 指代该对象。返回值为函数的最后一行或指定return表达式。


实例:有点类似于run(),let在使用中可用于空安全验证,变量?.let{}例如

val bean = listParkingVehicleEntity.find { it.plateNo == plateNo }
bean?.let {
parkingVehicleEntity = bean
showUserVehicle(it)
}
4.run

定义:有两种

fun <R> run(block: () -> R): R 
fun <T, R> T.run(block: T.() -> R): R


功能:执行传入的函数式,并返回函数的执行结果。​​run​​​的主要目的是强调需要执行的函数。函数内可通过​​this​​引用当前对象; 返回值为最后一行, 可以与当前对象类型不同


实例:

从​​intent​​​取​​EXTRA_URL​​​的值,不为非空且内容不为空,赋值给​​url​​。否则弹出提示并关闭页面。

url = intent.getStringExtra(EXTRA_URL)?.takeIf{it.isNotEmpty()}?:run{
toast("不能浏览一个空链接哦")
activity.finish()
}

"kotlin".run{"Hello $this"}
>>> Hello kotlin

小结:run()、with(T)、T.run()、 T.let()、 T.apply() 、T.also()选择时的流程图参考如下:

Kotlin在实际项目中的使用小结_初始化

5.takeIf和takeUnless

定义:

/**
* Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't.
*/
fun <T> T.takeIf(predicate: (T) -> Boolean): T?


功能:传递一个函数参数,如果函数结果为true,返回T对象,否则返回null。


实例1:

"123".takeIf {it.length > 10}

//返回值为null

//takeUnless与takeIf相反,参数函数返回false时返回T对象,否则返回null
"123".takeUnless{it.length > 10}
//返回值为123

实例2:

var file = File("filePath")
if(file.exists()){
//do something
}else{
return false
}

//引入takeIf 后
var file = File("filePath").takeIf{it.exists()}?:return false
//do something

八 、类型别名(Type alias)的使用

自kotlin 1.1起,类型别名(Type alias)为现有类型提供替代名称,

如果类型名称太长,可引入较短别名替代原类型名。

1.为集合类型(collection type)提供别名
//缩短较长泛型类型(generic type)是很有吸引力的
typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>
2.为函数类型(function type)提供别名(alias)
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean
3.为内部类(inner)和嵌套类(nested)创建别名
class A{
inner class Inner
}
class B{
inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner

提示:

类型别名不会引入新类型,等效于相应底层类型,编译器会把别名翻译为原有类型:

//添加别名声明typealias Predicate<T>后,Kotlin编译器总是把它扩展为(Int) -> Boolean     
typealias Predicate<T> = (T) -> Boolean
fun foo(p: Predicate<Int>) = p(42)

fun main(args: Array<String>) {
//类型别名和原有类型,可以相互替代,因为编译器会把别名翻译为原有类型
val f: (Int) -> Boolean = { it > 0 }
println(foo(f)) // 输出 "true"

val p: Predicate<Int> = { it > 0 }
println(listOf(1, -2).filter(p)) // 输出 "[1]"
}

实例运用:如在网络请求中,有时为了命名的规范性,可以适当引入别名来实现。

typealias CZGetRequest = GetRequest

typealias CZPostRequest = PostRequest

参考:

​1.慎用延迟初始化(lazy initialization)​

​2.Kotlin with 、apply等函数的使用场景总结​

​3.Kotlin官方文档介绍Type aliases​