从 JAVA 到 Kotlin

自Kotlin被google官方认可之后,Android界对Kotlin的热度便持续上涨,在了解了它的优势后,作为一名与时俱进的Android开发工程师,是时候有必要来学习Kotlin了。

Kotlin有何优势?

  • 代码的大幅度精简
  • 100% 兼容 Java 代码

以上两点是我认为Kotlin的最大优势,目前java 的主流开发工具基本都支持了java转Kotlin,因此转换基本不需要什么成本,就算在使用过程中遇到坑,完全可以用java来写,无隔阂的运行,一开始不熟悉的时候还可以用java编写后转成Kotlin,不断对比就会逐渐熟悉。(ps:一个好的IDE对学习也是非常有帮助,在写Kotlin的代码时android studio 经常会提醒我有简化的写法)

Kotlin基础语法

Kotlin教程

Kotlin在线学习工具
Kotlin 中文站

对比学习

kotlin中一切皆为对象 ,没有像java一般的基本数据类型,数值类型为:Int, Float, Double等都是对象(类似java的包装类);函数也是对象,可作为参数和返回值。

定义常量与变量

可变变量定义:var 关键字

var <标识符> : <类型> = <初始化值>

不可变变量定义:val 关键字,只能赋值一次的变量(类似Java中final修饰的变量)

val <标识符> : <类型> = <初始化值>

类型检测及自动类型转换

我们可以使用 is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)。

fun getStringLength(obj: Any): Int? {
  if (obj is String) {
    // 做过类型判断以后,obj会被系统自动转换为String类型
    return obj.length 
  }
}
  • Kotlin中的Any = Java中的Object

类型强转

类型强转在Android中非常常见,如:

GridView gridView = (GridView) findViewById(R.id.grid_view);

而Kotlin中使用as关键词:

val recyclerView = findViewById(R.id.recycler_view) as RecyclerView

字符串模板

$ 表示一个变量名或者变量值
$varName 表示变量值
${varName.fun()} 表示变量的方法返回值:

var a = 1
// 模板中的简单名称:
val s1 = "a is $a" 

a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"

static : companion object

java中static表示类相关的,生命周期于类相同,Android中经常会用来声明一些static常量和工具类的static方法

public static final int A = 1;
public static final String STR = "abc";

public static int getInt() {
        return A;
    }

在Kotlin中则没有static关键字,取而代之的是companion object (伴生对象)

companion object{
        val A = 1 //val表示不可变,类似java的 final
        val STR = "abc"
    }

在Android中常见的utils类(只有静态方法)可以变成这样:

class Utils private constructor() {

    init {
        throw AssertionError()
    }

    companion object {
        fun staticFun() {/*do someting*/
        }
    }
}

当然,这是java思维下Kotlin转变,实际上用Kotlin实现工具类还有其他的方法,其一是使用object:

object Utils {

    /** 获得状态栏的高度  */
    @JvmStatic
    fun getStatusHeight(context: Context): Int {
        var statusHeight = -1
        try {
            val clazz = Class.forName("com.android.internal.R\$dimen")
            val obj = clazz.newInstance()
            val height = Integer.parseInt(clazz.getField("status_bar_height").get(obj).toString())
            statusHeight = context.resources.getDimensionPixelSize(height)
        } catch (e: Exception) {
            e.printStackTrace()
        }

        return statusHeight
    }
}

注意@JvmStatic,代表了在Java调用时也可以像static方式那样使用:Utils.getStatusHeight() ,如果不加这个注解的话在java中调用Utils.INSTANCE.getStatusHeight()
还有另一种方式顶层函数,也是Kotlin实战这本书中推荐的方式,不需要创建类,而是直接创建一个.kt文件定义函数(方法):

@file:JvmName("ScreenUtil")//定义java中的类名

package com.huburt.library.util

import android.content.Context

/**
 * Created by hubert
 *
 * Created on 2017/11/2.
 */
fun getStatusHeight(context: Context): Int {
    var statusHeight = -1
    try {
        val clazz = Class.forName("com.android.internal.R\$dimen")
        val obj = clazz.newInstance()
        val height = Integer.parseInt(clazz.getField("status_bar_height").get(obj).toString())
        statusHeight = context.resources.getDimensionPixelSize(height)
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return statusHeight
}

Kotlin中可以直接使用该方法,会自动导入import com.huburt.library.util.getStatusHeight,java中使用还是会像使用static方法一样ScreenUtil.getStatusHeight(this);,如果没有定义java类名@file:JvmName("ScreenUtil")的话,会默认使用.kt的文件名+Kt,如这里我是放在ScreenUtil.kt文件中,使用就是ScreenUtilKt.getStatusHeight(this);

既然Kotlin没有static关键字,java中较多使用的

单例模式

class Singleton {
    private Singleton() {
        System.out.print("This is a singleton");
    }

    private static class Holder {
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.instance;
    }
}

在Kotlin中也需要作出改变

class Singleton private constructor() {
    init {
        println("This ($this) is a singleton")
    }

    private object Holder {
        val INSTANCE = Singleton()
    }

    companion object {
        val instance: Singleton by lazy { Holder.INSTANCE }
    }
}

构造

可以看到Kotlin的constructor(构造器)位置与java中不同。
Koltin 中的类可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后:
class Person constructor(firstName: String) {}
如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略。
class Person(firstName: String) {}
主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀。

class Person constructor(firstName: String) {
    init {
        System.out.print("FirstName is $firstName")
    }
}

注意:主构造器的参数可以在初始化代码段中使用,也可以在类主体n定义的属性初始化代码中使用。 一种简洁语法,可以通过主构造器来定义属性并初始化属性值(可以是var或val):

class People(val firstName: String, val lastName: String) {
    //...
}

如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:

class Person() {
    var parent: Person? = null

    constructor(parent: Person) : this() {
        this.parent = parent
    }
}

对象

Kotlin中创建对象不需要 new ,只需要调用构造器如:var person = Person()

匿名内部类

Android中对于匿名内部类的使用也非常多,典型的setOnClickLsitener ,而在Kotlin中使用object关键字实现匿名内部类:

button.setOnClickListener(object: OnClickListener {
    override fun onClick(v: View?){}
})

javaBean

javaBean也是Android开发中非常常见的,用于封装数据,这是一个基本的网络结果实体类

public class BaseEntity<T> {
    private int code;
    private String message;
    private T data;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

用Kotlin去声明的话:

class BaseEntity<T> {
    var code: Int = 0
//        set //默认实现
//        get
    var message: String? = null
    var data: T? = null

}

不要认为这只是java中public声明的属性,这些属性都有默认实现的getter/setter,如果不想某个属性被修改(只读)只需要在set前声明 private

还有另外一种更简便的写法,Kotlin中定义了一种数据类,可以创建一个只包含数据的类,使用关键字为 data:

data class DataClassExample (val x: Int, val y: Int, val z: Int)

编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:

  • equals() / hashCode()
  • toString() 格式如 "User(name=John, age=42)"
  • componentN() functions 对应于属性,按声明顺序排列
  • copy() 函数

如果这些函数在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。

为了保证生成代码的一致性以及有意义,数据类需要满足以下条件:

  • 主构造函数至少包含一个参数。
  • 所有的主构造函数的参数必须标识为val 或者 var ;
  • 数据类不可以声明为 abstract, open, sealed 或者 inner;
  • 数据类不能继承其他类 (但是可以实现接口)。

var message: String? 是否对这个?感到奇怪。其实这是表示这个属性可能为null,在使用的时候需要进行处理。

NULL检查机制

在Android中使用变量前我们需要对其进行空判断避免空指针,这样往往带来带来大量的工作,这些空判断代码本身没有什么实际意义,并且让代码的可读性和简洁性带来了巨大的挑战。除此之外,我们经常会忘记判空!!!

Kotlin 为了解决这个问题,它并不允许我们直接使用一个可选型的变量去调用方法或者属性。对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理

//类型后面加?表示可为空
var age: String? = "23" 
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1 //等价于 if (age != null) age.toInt() else -1

方法(函数)

java中的方法:

public int funName() {
        return 1;
    }

而在Kotlin函数定义使用关键字 fun,参数格式为:参数 : 类型

fun sum(a: Int, b: Int): Int {   // Int 参数,返回值 Int
    return a + b
}

可变长参数函数

java中使用...表示可变参数
函数的变长参数可以用 vararg 关键字进行标识:

fun vars(vararg v:Int){
    for(vt in v){
        print(vt)
    }
}

todo

之前用java开发Android的时候,对于一些放在之后实现的功能,为了防止忘记,会加上//TODO,以方便自己后续记得在此处补上实现。而在Kotlin中,输入todo,IDE提示的是一个TODO("xxx")的函数,功能类似,只是在调用此处方法的时候回抛出一个异常:

kotlin.NotImplementedError: An operation is not implemented: xxx
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容