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

推荐阅读更多精彩内容