var uuid:String
get() = CacheTool.localCache.getString(UUID)?:""
set(value) {
CacheTool.localCache.put(UUID,value)
}
如不想对外公开某个方法时,可以使用修饰符private
实现,如:
// 是否加载失败
var isLoadFail = false
private set
设置 set 属性为private 后, 那么它将只在同一个源代码文件内可以访问,在其他作用域下 如其他包名下不能访问。
注意:这种方式只适用于 set 方法, get的访问权限默认和属性是一致的,如下面的使用会编译错误
var isLoadFail = false
private get //// 编译错误,get的访问权限和属性一致
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 (x){
0,1 -> print("x==0 or x==1")
else -> print("otherwise")
}
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 Activity.bindView(id: Int): Lazy = lazy {
viewFinder(id) as V
}
//acitivity中扩展调用
private val Activity.viewFinder: Activity.(Int) -> View?
get() = { findViewById(it) }
//在activity中的使用姿势
val mTextView by bindView(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 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()
}
}
})
}
定义:
funwith(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
}
定义:
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中。
定义:
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)
}
定义:有两种
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()选择时的流程图参考如下:
定义:
/**
* Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't.
*/
fun 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
自kotlin 1.1起,类型别名(Type alias)为现有类型提供替代名称,
如果类型名称太长,可引入较短别名替代原类型名。
//缩短较长泛型类型(generic type)是很有吸引力的
typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean
class A{
inner class Inner
}
class B{
inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner
提示:
类型别名不会引入新类型,等效于相应底层类型,编译器会把别名翻译为原有类型:
//添加别名声明typealias Predicate后,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