Kotlin 轻量级Android开发

Kotlin 是什么

Kotlin是一门运行在JVM之上的语言。

它由Jetbrains创建,而Jetbrains则是诸多强大的工具(如知名的Java IDE IntelliJ IDEA)背后的公司。Kotlin是一门非常简单的语言,其主要目标之一就是提供强大语言的同时又保持简单且精简的语法。
在这里插入图片描述

kotlin 特性

其主要特性如下所示:

  1. 轻量级:这一点对于Android来说非常重要。项目所需要的库应该尽可能的小。Android对于方法数量有严格的限制,Kotlin只额外增加了大约6000个方法。
  2. 互操作:Kotlin可与Java语言无缝通信。这意味着我们可以在Kotlin代码中使用任何已有的Java库;因此,即便这门语言还很年轻,但却已经可以使用成百上千的库了。除此之外,Kotlin代码还可以为Java代码所用,这意味着我们可以使用这两种语言来构建软件。你可以使用Kotlin开发新特性,同时使用Java实现代码基的其他部分。
  3. 强类型:我们很少需要在代码中指定类型,因为编译器可以在绝大多数情况下推断出变量或是函数返回值的类型。这样就能获得两个好处:简洁与安全。使用var声明可变参数,val声明不可变参数 varvar是一个可变变量,这是一个可以通过重新分配来更改为另一个值的变量。这种声明变量的方式和Java中声明变量的方式一样。 val: val是一个只读变量,这种声明变量的方式相当于java中的final变量。一个val创建的时候必须初始化,因为以后不能被改变。 上段代码: var name = "ztz" println(name) name = "yif" println(name) val finalValue = "i v"; println(finalValue); 结果如下: ztz yif iv
  4. 空安全:Java最大的一个问题就是null。如果没有对变量或是参数进行null判断,那么程序当中就有可能抛出大量的NullPointerException,然而在编码时这些又是难以检测到的。Kotlin使用了显式的null,这会强制我们在必要时进行null检查。
  5. 语法糖
    • 类的简化,隐式getter()、setter()
    • 接口的默认实现
    • lambda与高阶函数
    • 空指针安全,编译时期的空指针检查
    • 流式集合操作 map(), forEach()
  6. 新特性
    • 函数拓展、属性拓展
    • 属性代理

Kotlin 轻量级开发

Null安全

Kotlinnull安全的。如果一个类型可能为null,那么我们就需要在类型后面加上一个?。这样,每次在使用该类型的变量时,我们都需要进行null检查。比如说,如下代码将无法编译通过:

var artist: Artist? = null?
artist.print()

第2行会显示一个错误,因为没有对变量进行null检查。我们可以这样做:

if (artist != null) {
    
    
? artist.print()?
}

这展示了Kotlin另一个出色的特性:智能类型转换。如果检查了变量的类型,那就无需在检查作用域中对其进行类型转换。这样,我们现在就可以在if中将artist作为Artist类型的变量了。这对于其他检查也是适用的。还有一种更简单的方式来检查null,即在调用对象的函数前使用?。甚至还可以通过Elvis运算符?提供另外一种做法:

val name = artist?.name ?: ""

数据类data

Java中,如果想要创建数据类或是POJO类(只保存了一些状态的类),我们需要创建一个拥有大量字段、getterssetters的类,也许还要提供toStringequals方法:

public class Artist {
    
    
    private long id;
    private String name;
    private String url;
    private String mbid;
 
    public long getId() {
    
    
        return id;
    }
 
    public void setId(long id) {
    
    
        this.id = id;
    }
 
    public String getName() {
    
    
        return name;
    }
 
    public void setName(String name) {
    
    
        this.name = name;
    }
 
    public String getUrl() {
    
    
        return url;
    }
 
    public void setUrl(String url) {
    
    
        this.url = url;
    }
 
    public String getMbid() {
    
    
        return mbid;
    }
 
    public void setMbid(String mbid) {
    
    
        this.mbid = mbid;
    }
 
    @Override public String toString() {
    
    
        return "Artist{
    
    " +
                "id=" + id +
                ", name='" + name + '\'' +
                ", url='" + url + '\'' +
                ", mbid='" + mbid + '\'' +
                '}';
    }
}

Kotlin中,上述代码可以写成下面这样:

data class Artist (?
    var id: Long,
    var name: String,
    var url: String,
    var mbid: String)

Kotlin使用属性而非字段。基本上,属性就是字段加上其gettersetter

互操作

Kotlin提供了一些非常棒的互操作特性,这对于Android开发帮助非常大。其中之一就是拥有单个方法的接口与lambda表达式之间的映射。这样,下面这个单击监听器:

view.setOnClickListener(object : View.OnClickListener {
    
    
    override fun onClick(v: View) {
    
    
        toast("Click")?
    }
?})

可以写成这样:

view.setOnClickListener {
    
     toast("Click") }

此外,getterssetters都会自动映射到属性上。这并不会造成性能上的损失,因为字节码实际上只是调用原来的getterssetters。如下代码所示:

supportActionBar.title = title
textView.text = title
contactsList.adapter = ContactsYifAdapter()

Lambda表达式

Lambda表达式会在极大程度上精简代码,不过重要的是借助于Lambda表达式,我们可以做到之前无法实现或是实现起来非常麻烦的事情。借助于Lambda表达式,我们可以以一种更加函数式的方式来思考问题。Lambda表达式其实就是一种指定类型,并且该类型定义了一个函数的方式。比如说,我们可以像下面这样定义一个变量:

val listener: (View) -> Boolean

该变量可以声明一个函数,它接收一个view并返回这个函数。我们需要通过闭包的方式来定义函数的行为:

val listener = {
    
     view: View -> view is TextView }

上面这个函数会接收一个View,如果该viewTextView的实例,那么它就会返回true。由于编译器可以推断出类型,因此我们无需指定。还可以更加明确一些:

val listener: (View) -> Boolean = {
    
     view -> view is TextView }

借助于Lambda表达式,我们可以抛弃回调接口的使用。只需设置希望后面会被调用的函数即可:

fun asyncOperation(value: Int, callback: (Boolean) -> Unit) {
    
    
    ...
    callback(true)?
}
asyncOperation(5) {
    
     result -> println("result: $result") }

还有一种更加简洁的方式,如果函数只接收一个参数,那就可以使用保留字it

asyncOperation(5) {
    
     println("result: $it") }

高阶函数

lambda`本身作为一等公民,它是有类型的。比如下面这个加法表达式sum的类型为`(Int, Int) -> Int。
val sum: (Int, Int) -> Int = { x, y -> x+y }

一个变量有类型是再自然不过的事。而高阶函数的入参与返回值既然是lambda,那其类型奇怪一点也很正常。

fun main(args: Array<String>) {
    
    
    // 自定义高阶函数, lambda 表达式 作为入参
    listOf("1", "2", "3", "4").myForEach {
    
     println(it) }

    // 自定义高阶函数, lambda 表达式 作为返回值
//    getLogger()("I'm a Closure")
    var logger = getLogger()
    logger("I'm a Closure")
}

/**
 * 接受一个 lambda 表达式, 作为遍历任务
 */
fun <T> List<T>.myForEach(doTask: (T) -> Unit){
    
    
    for(item in this)
        doTask(item)
}

/**
 * 返回一个 lambda 表达式(闭包), 如: 日志输出工具 logger
 */
fun getLogger(): (String) -> Unit{
    
    
//    return { println(it) }
    return fun (it: String){
    
    
        println(it)
    }
}

PS: 看到getLogger()这种用法,你大概意识到可以像js那样写闭包了。

接口的默认实现

顾名思义,它便是指接口可以和抽象类一样,有方法体的默认实现。 我把它归结在语法糖里,是因为java8中早已有了一模一样的东西,对应的关键字叫default

interface A {
    
    
    fun foo() {
    
     println("A") }    // 默认实现, 打印"A"
    fun bar()
}
 
interface B {
    
    
    fun foo() {
    
     println("B") }
    fun bar() {
    
     println("bar") }
}
 
// 多继承时,显式指定 super<A>.foo() 以去冲突
class D : A, B {
    
    
    override fun foo() {
    
    
        super<A>.foo()
        super<B>.foo()
    }
 
    override fun bar() {
    
    
        super.bar()
    }
}

流式集合操作 map(), forEach()

流式集合操作符,算是很普遍了,任何语言里都有,然而不支持函数式的话,写起来比较臃肿。 如下例子,一些操作符的衔接,使得操作逻辑十分清晰,之后需求变动,比如降序改为升序,也只需改动sortedDescending()一行,十分灵活。

fun main(args: Array<String>){
    
    
    val list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    list.filter {
    
     it%2==0 }             // 取偶数
            .map{
    
     it*it }               // 平方
            .sortedDescending()         // 降序排序
            .take(3)                    // 取前 3 个
            .forEach {
    
     println(it) }    // 遍历, 打印
}
 
// 输出:
100
64
36

拓展

拓展这个东西,貌似是以装饰者模式来做的。它的效果是在不改源码的基础上,添加功能。比如我们要在Activity上加一个toast(),完全不用卸载基类里。这样简化了很多工作,尤其是对一些已打成jar包的类。

fun Activity.toast(msg: String) {
    
    
    Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}
/**
 * @author yif
 *
 * 函数拓展, 属性拓展
 */
fun main(args: Array<String>) {
    
    
    val list = listOf("1", "2", "3", "4")
 
    // 函数拓展
    list.myForEach {
    
     println(it) }
 
    // 属性拓展
    println("last: ${
    
    list.lastItem}")
}
 
/**
 * 拓展 List 类, 加一个自定义的遍历方法
 */
fun <T> List<T>.myForEach(doTask: (T) -> Unit){
    
    
    for(item in this)
        doTask(item)
}
 
/**
 * 拓展 List 类, 加一个自定义的长度属性
 */
val <T> List<T>.lastItem: T
        get() = get(size - 1)
 
// 输出:
1
2
3
4
last: 4

属性代理

它把属性的get()set()代理给了一个类,以便可以在get()set()时做一些额外的操作。如:

  • 懒加载
  • 观察者(属性变化时,自动发出通知)
  • 属性非空判断 以懒加载为例,lazySum可能需要复杂的运算,我们把它代理给lazy。 可以看到,只有第一次加载进行了计算,之后都是直接取值,提高了效率。 val lazySum: Int by lazy { println("begin compute lazySum …") var sum = 0 for (i in 0…100) sum += i println("lazySum computed!n") sum // 返回计算结果 } fun main(args: Array<String>) { println(lazySum) println(lazySum) } // 输出: begin compute lazySum … lazySum computed! 5050 5050 Anko AnkoKotlin团队开发的一个库,旨在简化Android开发。其主要目标在于提供一个DSL,使用Kotlin代码来声明视图: verticalLayout { val name = editText() button("Say Hello") { onClick { toast("Hello, ${name.text}!") } } } 它还提供了其他一些很有用的特性。比如说,导航到其他Activity和传值到其它界面: startActivity("id" to res.id, "name" to res.name) startActivity<ContentActivity>( Pair(Constants.CONTENT_TITLE_KEY, useWebsiteBean.name), Pair(Constants.CONTENT_URL_KEY, useWebsiteBean.link), Pair(Constants.CONTENT_ID_KEY, useWebsiteBean.id) ) 总结 Kotlin真的在很多方面都简化了Android的开发工作。它会提升你的生产力,并且可以通过非常不同且更加简单的方式来解决一些常见的问题。让我们一起使用Kotlin来快速敏捷开发吧。

猜你喜欢

转载自blog.csdn.net/hdbdhdbh/article/details/131286043
今日推荐