zoukankan      html  css  js  c++  java
  • Kotin与java的纠缠史

    Kotlin与java的纠缠史 - 格式化版

    1. 背景

    Jetbrains早在2010年推出Kotlin,在今年(2017)5月18,谷歌在今日举行的I/O开发者大会上宣布,将Kotlin语言作为安卓开发的一级编程语言,这个可爱的语言可于Java进行无缝混编。之前做过一段时间的C#和python开发,感觉三者之间总两两相似,不清楚是谁在模仿谁,谁在吃着谁的语法糖。

    2. 差异与对比

    2.1 构造器

    java Kotlin
    构造器 主构造器
    - 次构造器
    • java版:
    public class Model {
    
        public Model(){}
    
        public Model(String name){}
    }
    
    • Kotlin版:
      • 主构造器
    class KtModel constructor(name: String)
    
    ps:如果不私有化构造器或者注解构造器,constructor可以省略,即:
    
     -> class KtModel (name: String)
    

    由于Kotlin主构造器写法的出现,可以简化Java中JavaBean,关键字:data

    data class KtModel(var id: Int, var name: String)
    
    • Kotlin版
      • 次构造器
    class Model{
        var mName:String? = null
        constructor(name:String){
            mName = name
        }
    }
    
    //由于Kotlin中有参数缺省写法,比如:
    class Model{
        var mName:String? = null
        constructor(name:String,age:Int = 0){
            mName = name
        }
    }
    
    //在java中实例化这个Model还是需要:
    Model model = new Model("Test",20)
    
    //为了让java也享受到这个福利,Kotlin支持@JvmOverloads注解,为Java重载构造器
    class Model{
        var mName:String? = null
        @JvmOverloads
        constructor(name:String,age:Int = 0){
            mName = name
        }
    }
    
    //此时:java调用
    Model model = new Model("Test",20);
    Model model = new Model("Test");
    

    2.2 自定义setter和getter

    由data声明的javabean默认实现了getter方法,如果变量为var类型也同时实现了getter方法,并且像equal() hashcode() toString()方法也自动复写了。除此,也支持自定义setter和getter方法,写法和c#的方式一样。

    class Student(var name: String, var grade: Int) {
    
        // Kotlin中的enum
        enum class LEVEL {GOOD, JUSTSOSO }
        //默认规则赋值
        var level: LEVEL = if (grade > 90) LEVEL.GOOD else LEVEL.JUSTSOSO
            set(value) {
                field = value
            }
            get() = field
    }
    ps: 通过field代表set get的属性
    
    

    2.3 条件筛选

    java中对某一字段类型根据不同的值进行筛选,同使用Switch“语句”,如:

    switch (itemViewType) {
                case ITEM_VIEWTYPE_LINKMSG:
                    return new LinkMsgViewHolder(inflater);
                case ITEM_VIEWTYPE_TEXTMSG_BYME:
                    return new TextMsgByMeViewHolder(inflater);
                case ITEM_VIEWTYPE_TEXTMSG_BYOTHER:
                    return new TextMsgByOtherViewHolder(inflater);
                ...
                }
    

     kotlin中对某一字段类型筛选的关键字when,但它不属于语句,它属于表达式,真的是表达式哦。

    fun getWeekInfo(day: Int): String {
        //因为是函数 所以可以直接使用在return后面
        return when (day) {
            1, 2, 3, 4, 5 -> "工作日"
            6, 7 -> "周末"
            else -> "unknow"
        }
    }
    
    在Kotlin中几乎一切都是表达式,所以上面更有简化版:
    
    fun getWeekInfo(day: Int) = when (day) {
            1, 2, 3, 4, 5 -> "工作日"
            6, 7 -> "周末"
            else -> "unknow"  //与java的default不能,else必须写,除非密封类(sealed class)可以不写,后面会详细讲解。
    }
    

    难道仅仅这些就够了,错,还有厉害的!前方高能。

    fun getWeekInfo(student: Student) = when {
        student.name.equals("马云") -> "考什么试!"
        student.grade >= 60 -> "Pass"
        student.grade < 60 -> "fail"
        student is Student -> "is 相当于 instanceof"
        else -> "unknow"
    }
    
    1. when可以不一定带参数,就使用方法参数
    2. when中的条件不需要同一种方式判断,只需要返回boolean类型
    

    2.4 循环结构

    语言 次数迭代 对象池迭代
    java 支持 支持
    kotlin 强支持 支持

    java版本两种循环方式的写法

    for (int i = 0; i < 10; i++) {
            //TODO
        }
    
    for (String arg : args) {
            //TODO
        }
    

    kotlin版本,与python写法相似

    // 次数迭代
    for(i in 1..10){  //[1,10]区间往上遍历
            print(i)
        }
    
    for(i in 10 downTo 1){ // [1,10]区间往下遍历
            print(i)
        }
        
    for(i in 1 until 10){ // [1,10)区间往上遍历
            print(i)
        }
    
    
    for(i in 1..10 step 2){ //设置步数往上走
            print(i)
        }
        
        
        
    //对象池迭代
    for (arg in args){
            print(arg)
        }
    

    2.5 引入静态方法

    引入静态方法的目的:

    java Kotlin
    工具方法 类的扩展

    引入方式:

    • java: import static com.meelive.ingkee.base.utils.guava.Preconditions.checkArgument;
    • Kotlin: import com.Test.utils.showToast

    静态方法的写法

    • java:纯工具类的写法
    • kotlin:
    fun Context.showToast(message: String) : Toast {
        var toast : Toast = Toast.makeText(this,message,Toast.LENGTH_SHORT)
        toast.setGravity(Gravity.CENTER,0,0)
        toast.show()
        return toast
    }
    
    作为Context类的拓展方法,在Activity,Service中,可以直接使用,面向对象的封装性显得更加的严实(个人看法)。
    

    2.6 类型转换

    java中对于类型的转化,写的特别想吐的语句如下:

    Person p = new Person();
    if(p instanceof Student){
        Student student = (Student) p;
        String id = student.uid;
    }
    

    然后看看Kotlin的模仿版:

    val p = Person()
    if (p is Student) {
        val student = p as Student
        var uid = student.uid
    }
    

    瞪大眼睛看看简化版,找不同的时间到了!

    val p = Person()
    if (p is Student) {
        var uid = p.uid
    }
    
    is 进行判断后,Person类实例就已经装成Student类实例了,这就是Kotlin的智能类型转化。
    

    2.7 单例

    • java版单例(懒汉式和饿汉式),懒汉式如下:
    public static RoomManager ins() {
        return SingletonHolder.INSTANCE;
    }
    
    private static class SingletonHolder {
        private static final RoomManager INSTANCE = new RoomManager();
    }
    
    • kotlin版
    class AppManager private constructor(var info: String) {
        companion object {
            private var INSTANCE: AppManager? = null
            fun getInstance(info: String): AppManager {
                if (INSTANCE == null) {
                    synchronized(AppManager::class.java) {
                        if (INSTANCE == null) {
                            INSTANCE = AppManager(info)
                        }
                    }
                }
                return INSTANCE!!
            }
        }
        
        fun getPhoneInfo(arg: String): String? {
            return null
        }
    }
    

    如果是无参单例,有更为简单的写法

    /**
     * Created by YangLang on 2017/7/29.
     */
    object AppManager {
    
        fun getPhoneInfo(arg: String): String? {
            return null
        }
    }
    

    2.8 正则表达式

    • java的写法
    String info = "12,34,56.789";
    String[] split = info.split("[,.]");
    for (String s : split) {
        System.out.println(s);
    }
    
    java默认使用正则去切割
    
    • Kotlin写法
     var info = "12,34,56.789"
        for (s in info.split(",",".")) {
            println(s)
        }
        
    kotlin 为了编程更加口头化,生活化,默认不使用正则,上面写法的理解是,把info用","或者"."进行分割。
    
    //进一步说明
    info.split(".")
    1. 在java中输出为空,因为.在正则规则中为统配符
    2. 在kotlin中输出为12,34,56和789,因为Kotlin默认不使用正则,所以.只是一个普通的字符。
    

    2.9 嵌套类和内部类

    说明: 嵌套类不含有外部类的引用,内部类拥有外部类的引用,正因为这样,也可以造成Activity没有释放,而内存泄漏问题。

    • java
    public class Outter {
    
       //包含Outter类的引用,可以使用Outter的方法
        class Intter{}
    
        // 不包含Outter类引用,不能使用Outter的方法
        static class NotIntter{}
    }
    
    • Kotlin
    class Outter {
    
        inner class Intter
    
        class NotIntter
    }
    

    java默认为内部类,Kotlin默认为嵌套类,一定程度的避免造成内存泄漏无意识写法。

    2.10 密封类

    密封类的概念和C#一样,密封类可以被继承,但本身不能被实例化。所有的子类必须在同一文件中,相当于把密封类的子类都封装在一块,可以配合when使用。

    sealed class BaseModel{}
    class FirstModel:BaseModel(){}
    class SecondModel:BaseModel(){}
    
    fun justDo(baseModel: BaseModel) = when(baseModel){
        is FirstModel -> ""
        is SecondModel -> ""
    }
    
    没有使用else,但是必须要写全baseModel的子类,如果编写期间新创建一个类继承BaseModel,此处when直接报错,密封类的这种特性,保证编写when表达式时,可以无一疏漏的考虑到每一种情况,约束代码的编写。
    

    2.11 代理

    代理方式语言 java Kotlin
    静态代理
    动态代理
    属性代理 ×
    标准代理 ×

    这里不讲java的设计模式,主要关注Kotlin的代理模式,从类代理、属性代理、标准代理三个方面

    • 类代理

    假如我们需要创建AnimatorListener的实例,Java中最常见的方式可能是匿名内部类,我们需要重载它的四个方法,虽然我们不都用,这样代码就显得多余。Kotlin中就可以用该接口得一个实现类代为重载,并处理自己的TODO。

    class MyAnimatorListener(var animatorListener: Animator.AnimatorListener):Animator.AnimatorListener by animatorListener{
    
        override fun onAnimationEnd(animation: Animator?) {
            //TODO 可以进行预处理
            animatorListener.onAnimationEnd(animation)
        }
    }
    
    接口Animator.AnimatorListener需要的方法由传入参数animatorListener实现了,该MyAnimatorListener也通过关键字by,将需要实现的方法让animatorListener代为实现,重载方法后可以做预处理操作。
    
    • 属性代理

    当使用val修饰属性时,表明属性为不可修改,代理类继承ReadOnlyProperty<被代理类,类型>类

    当使用var修饰时,属性可修改,代理类继承ReadWriteProperty<Water,Boolean>

    /**
     * Created by YangLang on 2017/7/29.
     */
    fun main(args: Array<String>) {
        var p = Person("Test", 21)
        println("是否已经成人: ${p.isAdult}")
    }
    
    class Person(var name: String, var age: Int) {
    
        var isAdult: Boolean by isAdultProperty()
    }
    
    class isAdultProperty : ReadWriteProperty<Person, Boolean> {
        override fun setValue(thisRef: Person, property: KProperty<*>, value: Boolean) {
        }
    
        override fun getValue(thisRef: Person, property: KProperty<*>): Boolean {
            return thisRef.age > 18
        }
    }
    
    • 标准代理

    Kotlin标准代理有lazy(),observable(),notNull().. 所有这样Kotlin自建代理都在delegates类下,主要讲lazy(懒加载) 在java中使用supplier和suppliers进行懒加载(延时求值),在kotlin中通过方法lazy直接延时求值,更快更强!!

    class InfoConfig {
        val info: String by lazy {
            "Testinfo"
        }
    }
    
    fun main(args: Array<String>) {
        print(InfoConfig().info)
    }
    
    第一次获取info值时,会通过lazy函数传入的函数参数进行求值,以后再获取该值时,直接返回,所以懒加载可进行复杂计算。
    

    2.12 属性赋初值问题

    属性不管申明为val还是var都必须赋初值,我们只能被迫写成 var info:String? = null。但是实际情况下有些属性不允许赋予无意义的初值如null,我总结有三种方式处理(可能不全面):

    • lateinit var info: String(只能用在var类型上)
    • class Student(var name: String, var age: Int){ val isAudit = age > 18} (可用在var或者val上,但只是在初始化时赋一次值~)
    • val info: String by lazy {
      "Test info"
      } (只能用在val类型上)

    2.13 lambda的概要(对象引用,数组用法)

    用lambda表达式创建函数“变形体”

     var add = { x: Int, y: Int -> x + y }
     print(add(1,2))
    

    lambda缩写演史

    // 1. 匿名内部类
    rb_home.setOnClickListener(object:View.OnClickListener{
                override fun onClick(v: View?) {
                }
    })
    
    // 2. 如果内部类中只有一个方法,缩写成lambda表达式
     rb_home.setOnClickListener({v -> Unit})
     
    // 3. 如果函数参数最后一个参数为lambda表达式,可以提取放在函数参数括号外面
     rb_home.setOnClickListener(){v -> Unit}
     
     // 4. 如果函数参数括号为空,可以省略
     rb_home.setOnClickListener{v -> Unit}
     
     // 5. 如果lambda表达式形参咩有使用,可以省略
     rb_home.setOnClickListener{Unit} <==> rb_home.setOnClickListener{//TODO}
    
    

    可以根据以上的省略原则可以定义我们自己的的函数。如在java中做版本的判断,我们会写:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        webSettings.setAllowUniversalAccessFromFileURLs(true);
    }
    
    这样写起来不流程,而且不易复用
    

    Kotlin上面可以这样定义函数

    inline fun UP_VERSIONJELLY_BEAN(function: () -> Unit){
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEA){
            function()
        }
    }
    
    在使用时,可以毫不犹豫的写成这样:
    UP_VERSIONJELLY_BEAN{
        //TODO
    }
    
    

    2.14 反射中优质提升

    public class Test<T> {
    
        public Test() {}
    }
    
    

    以上的泛型写法,在java中运行时由于类型擦除的原因,无法确定T的类型,很难获取该对象的字节码对象,不过在早两天看到Guava支持运行时获取泛型类型,通过TypeToken工具类。但是在Kotlin可以轻松解决这样问题,通过标注关键字reified。

    inline fun <reified T: Activity> Activity.newIntent() {
        val intent = Intent(this, T::class.java)
        startActivity(intent)
    }
    

    2.15 let apply run with的区别

    传送门地址

  • 相关阅读:
    关于在MAC上进行 LARAVEL 环境 Homestead 安装过程记录
    js 贷款计算器
    js 实现阶乘
    js 两点间距离函数
    composer Your requirements could not be resolved to an installable set of packages
    vue 项目优化记录 持续更新...
    vue 项目打包
    vue 真机调试页面出现空白
    vue 真机调试
    谈谈-Android状态栏的编辑
  • 原文地址:https://www.cnblogs.com/android-er/p/7257194.html
Copyright © 2011-2022 走看看