美文网首页
从 JAVA 到 Kotlin

从 JAVA 到 Kotlin

作者: 胡奚冰 | 来源:发表于2017-09-23 10:11 被阅读572次

    自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
    

    相关文章

      网友评论

          本文标题:从 JAVA 到 Kotlin

          本文链接:https://www.haomeiwen.com/subject/ekbfextx.html