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的区别

    传送门地址

  • 相关阅读:
    R set.seed()
    R tapply()
    R table
    清除R console中出现加号+
    r向量映射
    Java常识1
    IDEA配置
    SQL.字符串重叠项数量统计
    SQL.数据库内拆分字符串并返回数据表
    IDEA--TomCat配置
  • 原文地址:https://www.cnblogs.com/android-er/p/7257194.html
Copyright © 2011-2022 走看看