kotlin java对比

简介

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) {
    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)

operator fun Bookcase.iterator(): Iterator = books.iterator()

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

val list =ArrayList()
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) {
    inlineFunc("inlineFunc") {
        println("HaHa")
    }
}

运行结果为:

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

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

fun main(args: Array) {
 
   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 parseObject(String text, Class cls) {
    return JSON.parseObject(text, cls);
}
inline fun  parseObject(text: String?): T {
    return JSON.parseObject(text, T::class.java)
}

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

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

调用的时候

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

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

inline fun  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对比)