美文网首页
kotlin java对比

kotlin java对比

作者: 码农小龙 | 来源:发表于2020-09-27 10:06 被阅读0次

    简介

    Kotlin 是一个基于 JVM 的新的编程语言,由 JetBrains 开发。于2010年首次推出,次年开源。它与Java 100%互通,并具备诸多Java尚不支持的新特性

    设计目标

    创建一种兼容Java的语言

    让它比Java更安全,能够静态检测常见的陷阱。如引用空指针

    让它比Java更简洁 如高阶函数、扩展函数等

    变量、方法、类的定义

    变量常量定义

    可变变量定义:

    var 关键字

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

    不可变变量定义:

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

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

    方法定义

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

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

    表达式类型确定的值作为函数体,返回类型自动推断

    fun sum(a: Int, b: Int) = a + b
    fun equals(a: Int, b: Int) = false
    

    无返回值的函数(类似Java中的void):

    fun printSum(a: Int, b: Int): Unit { 
        print(a + b)
    }
    // 如果是返回 Unit类型,则可以省略:
    fun printSum(a: Int, b: Int) { 
        print(a + b)
    }
    

    匿名函数

    fun main(args: Array<String>) {
        val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
        println(sumLambda(1,2))  // 输出 3
    }
    

    类的定义和实例

    Kotlin 中使用关键字 class 声明类,后面紧跟类名:

    class Foo {  // 类名为 Foo
        // 大括号内是类体构成
    }
    

    我们也可以定义一个空类:

    class Foo
    

    类的属性可以用关键字 var 声明为可变的,否则使用只读关键字 val 声明为不可变。

    class Foo {
        var name: String = ……
        var url: String = ……
        var city: String = ……
    }
    

    属性可以放到构造函数里面

    class Foo( var name: String , var url: String , var city: String)
    

    数据类data class 自动获取需要的getters,setters,equals(),hashcode(),toString()和copy()函数

    data class Person(var name: String,var age: Int,var height: Float = 1.8f)
    

    等同的java class

    public final class Person {
        private String name;
        private int age;
        private float height;
     
        public Person(String name, int age, float height) {
            this.name = name;
            this.age = age;
            this.height = height;
        }
     
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
            this.height = 1.8f;
        }
     
        public String getName() {
            return name;
        }
     
        public void setName(String name) {
            this.name = name;
        }
     
        public int getAge() {
            return age;
        }
     
        public void setAge(int age) {
            this.age = age;
        }
     
        public float getHeight() {
            return height;
        }
     
        public void setHeight(float height) {
            this.height = height;
        }
     
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", height=" + height +
                    '}';
        }
     
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
     
            Person person = (Person) o;
     
            if (age != person.age) return false;
            if (Float.compare(person.height, height) != 0) return false;
            return name != null ? name.equals(person.name) : person.name == null
        }
     
        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + age;
            result = 31 * result + (height != +0.0f ? Float.floatToIntBits(height) : 0);
            return result;
        }   
    }
    

    实例化一个对象,不需要new关键字

    var foo = Foo()
    

    字符串模板

    $ 表示一个变量名或者变量值

    $varName 表示变量值

    ${varName.fun()} 表示变量的方法返回值:

    val person = Person("小明",12)
    val string = "person=[${person.name},${person.age}]"
    

    对比java

    Person person = new Person("小明",12);
    String string = "person="+"["+person.getName()+","+person.getAge()+"]";
    

    NULL检查机制

    Kotlin 在变量定义的时候就指定是否可空

    var name:String ="小明"
    var city:String?=null//用?表示可空
    
    String name ="小明"
    String city =null
    

    在一个方法中

    var length  =city.length()
    

    在编译期间就会报错,这样写就不会编译出错

    var length  =city?.length()
    

    如果用java来写

    int length =city.length()
    

    在编译期间是无法感知异常的,有可能在项目运行时就crash了

    类型检测及自动类型转换

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

    还可以这样

    fun getStringLength(obj: Any): Int? {
      // 在 `&&` 运算符的右侧, `obj` 的类型会被自动转换为 `String`
      if (obj is String && obj.length > 0)
        return obj.length
      return null
    }
    

    对比java 的代码,需要将数据类型强制转换

    public Integer getStringLength(Object object) {
        if (object instanceof String) {
          //需要强转一下
            return ((String) object).length();
        }
        return null;
    }
    

    扩展函数

    相信大家项目中有很多的util类,如StringUtils

    Kotlin有一个聪明的解决方案 , 扩展函数 ,帮助你摆脱所有的util类一劳永逸

    fun Context.toast(text: String) = Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
    

    在所有的Context子类中可以直接调用toast("somewords")

    基本数据类型也可以这样

    fun Int.dp2px():Int {
        return ......
    }
    

    代码中可以直接调用

    var px =12.dp2px()
    

    默认参数

    fun Date.format(format: String = "yyyy年MM月dd日 HH:mm"): String {
        return SimpleDateFormat(format, Locale.CHINA).format(this)
    }
    

    可以在代码中这样使用

    var dateString =Date().format()
    

    也可以

    var dateString =Date().format(“yyyy-MM-dd HH:mm”)
    

    高阶函数

    什么是高阶函数

    高阶函数就是以另一个函数作为参数或返回值的函数,Kotlin可以以lambda或参数引用作为参数或返回值,所以,任何以lambda或函数引用作为参数或返回值的都是高阶函数

    要使用Kotlin的高阶函数就必须遵循它的函数类型
    先来看一个简单的例子,这是一个简单的函数类型申明

    val sum = { x: Int, y: Int -> x + y }
    

    之所以能这么写得益于Kotlin的类型推导,它的显示写法是这样的:

    var sum:(Int,Int)-> Int = {x , y-> x + y}
    

    高阶函数的使用

    将函数类型作为参数,直接看代码

     fun getNumResult(result: (Int, Int) -> Int): Int {
            return result(1,2)                                                                                                                       
        }
    
    //调用
    var value = getNumResult({ a, b -> a + b })
    //方法里面最后一个参数是函数的时候可以省略
    var value = getNumResult{ a, b -> a + b }
    ==> value = 3
    
    var value = getNumResult{ a, b -> a * b }
    ==> value = 2
    

    android 中常用的设置点击事件就可以简单的写成

    view.setOnClickListener { v ->
            {
                //啊!我被点击了  
            }
    }
    //,当参数只有一个lambda参数的时候 可以省略
    view.setOnClickListener {
       //啊!我被点击了  
     }
    

    正常的java代码

    view.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
           //啊!我被点击了  
        }
    });
    

    高阶函数在集合中的应用

    val list = arrayListOf("aaa", "bb", "c","b")
    list.sortBy {
        it.length
    }
    val find = list.find {
        it.length == 1
    }
    val indexOfFirst = list.indexOfFirst { 
        it.startsWith("b")
    }
    val filter = list.filter {
        it.length == 1
    }
    

    等等

    运算符重载和迭代器重载

    运算符重载

    data class Area(var width: Int, var height: Int) {
        operator fun plus(other: Area): Area {
            return Area(this.width + other.width, this.height + other.height)
        }
    
        operator fun compareTo(other: Area): Int {
            return this.width * this.height - other.width * other.height
        }
    }
    
    val area1 = Area(6, 3)
    val area2 = Area(4, 5)
    print(area1 + area2)
    ==> Area(width=10, height=8)
    print(area1 > area2)
    ==>false
    

    常用的重载 加减乘除、compareTo等

    迭代器重载

    迭代器(iterator)是java中我们非常熟悉的东西了,数据结构如List和Set都内置了迭代器,我们可以通过它提供的方法类遍历访问一个聚合对象中的各个元素

    data class Book(val name: String)
    
    class Bookcase(val books: List<Book>)
    
    operator fun Bookcase.iterator(): Iterator<Book> = books.iterator()
    

    重载iterator方法后的类可以通过以下方法遍历

    val list =ArrayList<Book>()
    val case = Bookcase(list)
    for (book in case) {
        ...
    }
    

    单例

    java中单例的定义

    public class DataCenter {
        private static DataCenter sInstance;
    
        private DataCenter() {
        }
    
        public static DataCenter getInstance() {
            if (sInstance == null) {
                sInstance = new DataCenter();
            }
            return sInstance;
        }
    }
    

    Kotlin 单例实现只需要关键字object

    object DataCenter {
       
    }
    

    object 全局声明的对象只有一个

    内联函数

    当我们使用lambda表达式时,它会被正常地编译成匿名类。这表示每调用一次lambda表达式,一个额外的类就会被创建,并且如果lambda捕捉了某个变量,那么每次调用的时候都会创建一个新的对象,这会带来运行时的额外开销,导致使用lambda比使用一个直接执行相同代码的函数效率更低。

    如果使用inline修饰符标记一个函数,在函数被调用的时候编译器并不会生成函数调用的代码,而是 使用函数实现的真实代码替换每一次的函数调用

    内联函数如何运作

    当一个函数被声明为inline时,它的函数体是内联的,也就是说,函数体会被直接替换到函数被调用地方,下面我们来看一个简单的例子,下面是我们定义的一个内联的函数:

     fun inlineFunc(prefix : String, action : () -> Unit) {
        println("call before $prefix")
        action()
        println("call after $prefix")
    }
    

    我们用如下的方法来使用这个内联函数:

    fun main(args: Array<String>) {
        inlineFunc("inlineFunc") {
            println("HaHa")
        }
    }
    

    运行结果为:

    >> call before inlineFunc
    >> HaHa
    >> call after inlineFunc
    

    最终它会被编译成下面的字节码:

    fun main(args: Array<String>) {
     
       println("call before $prefix")
       println("HaHa")
       println("call after $prefix")
       
    }
    

    lambda表达式和inlineFunc的实现部分都被内联了,由lambda生成的字节码成了函数调用者定义的一部分,而不是被包含在一个实现了函数接口的匿名类中。

    内联扩展函数之let

    let函数通常作用就是避免写一些判断null的操作。

    let函数的使用的一般结构

    object.let{
       it.todo()//在函数体内使用it替代object对象去访问其公有的属性和方法
       ...
    }
    //另一种用途 判断object为null的操作
    object?.let{//表示object不为null的条件下,才会去执行let函数体
       it.todo()
    }
    

    内联函数之with

    with函数使用的一般结构

     with(object){
       //todo
     }
    

    适用于调用同一个类的多个方法或成员时,可以省去类名重复,直接调用类的方法即可

    val person =Person()
    with(viewHolder){
        tvName.setText(person.name)
      tvAge.setText(person.age)
    }
    
    
    

    内联扩展函数之apply

    apply函数使用的一般结构

    object.apply{
    //todo
    }
    

    apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值

    val person =Person().apply{
      age =12
      name ="小明"
    }
    

    泛型及reified函数

    reified 具体化的,对比java和kotlin中的实现

    public static <T> T parseObject(String text, Class<T> cls) {
        return JSON.parseObject(text, cls);
    }
    
    inline fun <reified T> parseObject(text: String?): T {
        return JSON.parseObject(text, T::class.java)
    }
    

    结合上面的扩展函数还可以这样

    inline fun <reified T> String?.parseObject(): T {
        return JSON.parseObject(this, T::class.java)
    }
    

    调用的时候

    val testJson = "{\"age\":22,\"name\":\"小明\"}"
    val person: Person = testJson.parseObject()
    

    利用这种特性还可以实现一个函数返回不同的数据类型

    inline fun <reified T> Int.times(): T? {
        return when (T::class) {
            Int::class -> (this * 2) as T
            String::class -> ("$this$this") as T
            else -> null
        }
    }
    
    val stringValue: String = 12.times()
    ==> "1212"
    val intValue: Int = 12.times()
    ==> 24
    

    ....

    相关文章

      网友评论

          本文标题:kotlin java对比

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