Android开发中Kotlin之于java那些不一样的功能

感觉Kotlin对java不仅是一点点的改变,而是一种完全不同的体检。习惯kotlin的简洁后,就知道java到底e有多啰嗦了。今天简单对比一下在Android开发中kotlin在语言本身上就有哪些好用的功能。

可观察属性Delegates.observable。在Oc中早已有的功能,非常方便在状态值切换时使用,再也不怕状态值改变时没有调用到关联的函数。

语法:

var max: Int by Delegates.observable(0) { property, oldValue, newValue -> 
  doSomething() 
}

在初始化时接受两个参数:初始值和修改时的处理程序。每当给属性赋值后会自动调用该处理程。在处理状态值时十分有用,不用再像Java里那样状态值每重新赋值就需要调用一次更新函数。现在只需要在初始化时添加好更新函数即可。

如在tab页切换时:

private var tabPosition: Int by Delegates.observable(0) { _: KProperty<*>, _: Int, tab: Int -> 
  if (tab == 0) { 
            tv_title_name.text = getString(R.string.title0)
            loadData0()
  ... 
        } else { 
            tv_title_name.text = getString(R.string.title1)
            loadData1()
  ... 
        }
    }

在后面代码中,不用再关心tabPosition修改后的更新,只管重新赋值就好了。

适用场景:状态切换,目标值监听,属性值改变时的联动操作】

扩展。能够扩展一个类的新功能而无需要继承该类,也不用使用如装饰器那样的设计模式。直接写直接引用。这是Oc中早已有的功能,而Java却一直用不上。以致于在java代码中,会出现一堆如StringUtils、ToastUtils..等静态工具类。使用时再将原对象当作参数传入,做一堆操作后再返回,产生大量冗余代码。有了扩展功能,能非常方便的对对象添加其他方法和使用代码封装。

语法:

函数扩展:直接以fun 原类名.扩展函数名创建一个函数即可,在函数值中使用this来访问原对象; 常用于对原对象功能的添加。如对常用的StringUtils的封装,如判断当前string是否为一个url:

fun String.isHttpUrl(): Boolean { 
  return !this.isEmpty() && (this.startsWith("http://") || this.startsWith("https://")) 
}

在使用时直接调用str.isHttpUrl()即可【AndroidStudio会自动import】

属性扩展:直接以val 属性名:get()=...来创建即可。如使用Retrofit封装一个Api对象:

val Api
  get() = 
        RetrofitManager.instance
                .retrofit(builder = DefaultRetrofitBuilder.builder)
                .create(Api::class.java)!!

非空判断,这个对于步步校验,一路非空判断的强迫症来说非常有用。用来摆脱Java中的xx==null,xx!=null的繁琐的判断。

  • ?.:相当于调用的if not null,如:id = item?.id,相当于if(item!=null) id=item.id else id=null,若想给个默认值,则可以使用:id = item?.id ?: 0
  • !!:非空断言运算符(!!)将任何值转换为非空类型,若该值为空则抛出异常。如在必须非空时可使用Api.login(LoginReq(usename,pwd)).load(activity!!)
  • isNullOrEmpty()/isNullOrBlank()/isNotEmpty()/isNotBlank()/isEmpty()/isBlank():大量的内部函数更方便操作对象值。

数据类:常用于数据解析实体。在Java中要对网络响应的数据解析通常都是先定义好的响应的实体类,再进行解析。而数据类能更方便的定义实体类,并自动添加好set/get方法。再也不需要大篇大篇的set/get。

  • 语法:data class A(val a:Int);对复杂的响应实体可嵌套定义,如:
 data class NewsDetailResp(val data: NewsDetail) : BaseResponse() { 
        data class NewsDetail(val total: Double, 
  val detail: ArrayList<Detail>) { 
             data class Detail( 
  val id: Long, 
  val userid: Long 
                )
}
}

lazy,延迟加载,这个在Java中也可通过延迟赋值来实现,但都没有kotlin来的直接好用。

如单例模式的写法:

 companion object { 
  val instance: UserManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { UserManager() } 
}

  • 在Andoird开发中更好用的功能在于对释放OnCreate()方法中的一大堆的初始化操作,如定义并初始化一个Adapter,可以直接在定义时赋值:
 private val adapter: AreaAdapter by lazy { AreaAdapter(this) } 

【需注意,这种使用了上下文context的延迟加载,只能在生命周期方法中调用,不能在后面的定义的值时引用。】

告别方法重载:在Java开发中,对于同名不能同参的函数即方法重载。若参数过多,在封装函数时会写好几个同名函数,造成大量代码冗余。而在Kotlin中,有了默认参数的支持,只需要定义一个函数即可。如:

fun share(activity: Activity, url: String, title: String = "", desc: String = "", shareResult: (Boolean) -> Unit = {}) {} 

lambda使用,无疑是对Java最期待而又用不上的功能,在Kotlin终于可以使用block当参数,省去了大量匿名内部类的创建。在Java中的回调只能先定义一个接口,外部定义一个匿名内部类做一个参数传入函数体,函数内部在做了一连串操作后再使用该参数执行。在Kotlin中只需定义一个block即可,如定义一个点击事件,并给个默认值:

  fun updateItem(item: Item, click: () -> Unit) {//没有默认实现,必须传入} 
  fun updateItem(item: Item, click: () -> Unit={}}) {//有空默认值,非必传} 
  fun updateItem(item: Item, click: (Int) -> Boolean={}}) {//带参数的block} 

  //保存传入的block 
  var click: () -> Unit={} 
  fun updateItem(item: Item, click: () -> Unit}) { 
        this.click = click
  //内部调用时使用 
        click.invoke()
    }
  //外部调用时直接使用 
    xx.updateItem(item){
  //click action 
    }

list的foreach:对列表的遍历,可完善代替for in的遍历。如:

list.forEach { println("it = $it") }

另外kotlin提供了大师的扩展函数用于对列表操作。如大家都知道在Java中不能在对一个List遍历时动态删除,否则会抛出ConcurrentModificationException异常,一般会建一个临时的list,保存需要删除的对象,在遍历结束后再调用removeAll来实现。而在Kotlin中可直接在在遍历中删除:

list.filter { it -> it == "a" }.forEach { list.remove(it) }

【另外Kotlin还添加对列表的排序(sort),转map(associate),去重(distinct),过滤(filter),反转(asReversed),查找(find),最值(max/min),递减(reduce),求和(sumBy/fold)等函数,非常方便集合的操作】

mapTo 动态初始化数组:用于遍历一个集合,并填充到另一个集合中。类似于Java下for循环遍历集合,再将新的对象add到新的集合中。如遍历响应数据填充坐标集合:

  val entries = ArrayList<Entry>() 
    (start until datas.size).mapTo(entries) {
  val x = datas[it].time.toFloat() 
  val y = datas[it].value.toFloat() 
  Entry(x, y) 
    }

with:引用一个实例,并调用多个方法。还可以在调用过程中做其他操作。如调用Retrofit实例:

val retrofit = with(Retrofit.Builder()) { 
  baseUrl(baseUrl) 
  addConverterFactory(builder.factory) 
            client(okHttpClient(builder))
            //do other something

            //rxJavaAdapter
  addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 
  build() 
        }

apply/takeIf/also/takeUnless/let:常用的几种扩展函数,非常方便对对象的判断及操作,附上使用表格:

函数名 定义 block参数 闭包返回返回值 函数返回值 extension 其他
repeat fun repeat(times: Int, action: (Int) -> Unit) Unit Unit 普通函数
with fun with(receiver: T, f: T.() -> R): R = receiver.f() 无,可以使用this Any 闭包返回 普通函数
run run(block: () -> R): R Any 闭包返回 普通函数
let fun T.let(f: (T) -> R): R it Any 闭包返回
apply fun T.apply(f: T.() -> Unit): T 无,可以使用this Unit this
run fun T.run(f: T.() -> R): R 无,可以使用this Any 闭包返回
also fun T.also(block: (T) -> Unit): T it Unit 闭包返回
takeIf fun T.takeIf(predicate: (T) -> Boolean): T? it Boolean this 或 null 闭包返回类型必须是Boolean
takeUnless fun T.takeUnless(predicate: (T) -> Boolean): T? it Boolean this 或 null 闭包返回类型必须是Boolean

kotlinx对xml的注解解析。在Android最繁琐莫过于对findViewById,也有很多的第三方框架如butterknife来的简化这部分代码。但在使用了kotlinx后才发现定义组件的对象都是多余的。只须在根项目下引用gradle插件:classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version",在xml定义了一个组件后,在代码时直接使用id来就可以了,根本不需要定义变量,再findViewById去赋值。如在xml中定义:

  <?xml version="1.0" encoding="utf-8"?> 
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:app="http://schemas.android.com/apk/res-auto" 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content"> 

  <TextView 
  android:id="@+id/tv_name" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_marginStart="16dp" 
  android:text="yoyo" 
  android:textColor="@color/text_black" 
  android:textSize="@dimen/text_16" 
  app:layout_constraintBottom_toBottomOf="parent" 
  app:layout_constraintLeft_toLeftOf="parent" 
  app:layout_constraintTop_toTopOf="parent" /> 

</android.support.constraint.ConstraintLayout>

在Activity中使用setContentView引用了该xml后,则可以直接使用tv_name.text="jim"这样的代码一赋值。若在自定义的view,需要init{}代码块中调用infalte函数引入xml资源,在如:

class ResultView(context: Context) : ConstraintLayout(context) { 
    init {
        inflate(context, R.layout.view_result, this) 
    }

    fun addListener(clickAction: () -> Unit) { 
        btn_result.setOnClickListener { clickAction.invoke() } 
    }
}

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

推荐阅读更多精彩内容