转载

重拾Kotlin(1)-变量、数据类型、函数

重拾Kotlin(1)-变量、数据类型、函数

一、Hello World

按照国际惯例,学习一门新的语言通常都是从“Hello World”开始的,在这里也不例外,先看下 Java 中的 main 方法如何用 Kotlin 来表示

fun main(args: Array<String>) {
    println("Hello World")
}
复制代码

从这里可以看出 Kotlin 相比 Java 有几点不同

  1. 用关键字 fun 来声明一个函数
  2. 参数类型写在变量名之后
  3. 函数可以定义在文件的最外层,不需要把它放在类中
  4. 数组就是类。和 Java 不同,Kotlin 没有声明数组类型的特殊语法
  5. 使用 println 代替了 System.out.println ,这是 Kotlin 标准库提供的对 Java 标准库函数的封装
  6. 可以省略代码结尾的分号

此外,Kotlin 的最新版本已经可以省略 main 方法的参数了

二、变量

在 Java 中,大部分的变量是可变的,意味着任何可以访问到这个变量的代码都可以去修改它。而在 Kotlin 中,变量可以分为 可变变量(var)不可变变量(val) 两类

声明变量的关键字有两个:

  • val(value)——不可变引用。使用 val 声明的变量不能在初始化之后再次赋值,对应的是 Java 中的 final 变量
  • var(variable)——可变引用。var 变量的值可以被改变,对应的是 Java 中的非 final 变量

不可变变量在赋值之后就不能再去改变它的状态了,因此不可变变量可以说是线程安全的,因为它们无法改变,所有线程访问到的对象都是同一个,因此也不需要去做访问控制,开发者应当尽可能地使用不可变变量,这样可以让代码更加接近函数式编程风格

此外,在 Kotlin 中一切都是对象,没有像 Java 中那样的原始基本类型,但 byte、char、integer、float 或者 boolean 等类型仍然有保留,但是全部都作为对象存在

看以下例子

fun main(args: Array<String>) {
    //只读变量即赋值后不可以改变值的变量,用 val 声明
    //声明一个整数类型的不可变变量
    val intValue: Int = 100

    //声明一个双精度类型的可变变量
    var doubleValue: Double = 100.0
}
复制代码

在声明变量时我们通常不需要指明变量的类型,这可以由编译器根据上下文自动推导出来

fun main(args: Array<String>) {
    //在声明变量时我们通常不需要指明变量的类型,这可以由编译器根据上下文自动推导出来
    val intValue = 100
    var doubleValue = 100.0

    //如果只读变量在声明时没有初始值,则必须指明变量类型
    val intValue2: Int
    intValue2 = 10
}
复制代码

三、数据类型

3.1、基本数据类型

与 Java 不同,Kotlin 并不区分基本数据类型和它的包装类,在 Kotlin 中一切都是对象,可以在任何变量上调用其成员函数和属性

对于基本类型,Kotlin 相比 Java 有几点特殊的地方

  • 数字、字符和布尔值可以在运行时表示为原生类型值,但对开发者来说,它们看起来就像普通的类
  • Kotlin 对于数字 没有隐式拓宽转换 ,而在 Java 中 int 可以隐式转换为 long
  • 在 Kotlin 中字符不能视为数字
  • Kotlin 不支持八进制
//在 Kotlin 中,int、long、float 等类型仍然存在,但是是作为对象存在的

    val intIndex: Int = 100
    //等价于,编译器会自动进行类型推导
    val intIndex = 100

    //数字类型不会自动转型,必须要进行明确的类型转换
    val doubleIndex: Double = intIndex.toDouble()
    //以下代码会提示错误,需要进行明确的类型转换
    //val doubleIndex: Double = intIndex

    val intValue: Int = 1
    val longValue: Long = 1
    //以下代码会提示错误,因为两者的数据类型不一致,需要转换为同一类型后才能进行比较
    //println(intValue == longValue)

    //Char 不能直接作为数字来处理,需要主动转换
    val ch: Char = 'c'
    val charValue: Int = ch.toInt()
    //以下代码会提示错误
    //val charValue: Int = ch

    //二进制
    val value1 = 0b00101
    //十六进制
    val value2 = 0x123
复制代码

此外,Kotlin 的可空类型不能用 Java 的基本数据类型表示,因为 null 只能被存储在 Java 的引用类型的变量中,这意味着只要使用了基本数据类型的可空版本,它就会被编译成对应的包装类型

//基本数据类型
    val intValue_1: Int = 200
    //包装类
    val intValue_2: Int? = intValue_1
    val intValue_3: Int? = intValue_1
    //== 比较的是数值相等性,因此结果是 true
    println(intValue_2 == intValue_3)
    //=== 比较的是引用是否相等,因此结果是 false
    println(intValue_2 === intValue_3)
复制代码

如果 intValue_1 的值是100,就会发现 intValue_2 === intValue_3 的比较结果是 true,这就涉及到 Java 对包装类对象的重复使用问题了

3.2、Any 和 Any?

Any 类型是 Kotlin 所有非空类型的超类型,包括像 Int 这样的基本数据类型

如果把基本数据类型的值赋给 Any 类型的变量,则会自动装箱

val any: Any = 100
    println(any.javaClass) //class java.lang.Integer
复制代码

如果想要使变量可以存储包括 null 在内的所有可能的值,则需要使用 Any?

val any: Any? = null
复制代码

3.3、Unit

Kotlin 中的 Unit 类型类似于 Java 中的 void,可以用于函数没有返回值时的情况

fun check(): Unit {

}

//如果返回值为 Unit,则可以省略该声明
fun check() {

}
复制代码

Unit 是一个完备的类型,可以作为类型参数,但 void 不行

interface Test<T> {
    fun test(): T
}

class NoResultClass : Test<Unit> {
    
    //返回 Unit,但可以省略类型说明,函数也不需要显式地 return 
    override fun test() {

    }

}
复制代码

3.4、Nothing

Nothing 类型没有任何值,只有被当做函数返回值使用,或者被当做泛型函数返回值的类型参数使用时才会有意义,可以用 Nothing 来表示一个函数不会被正常终止,从而帮助编译器对代码进行诊断

编译器知道返回值为 Nothing 类型的函数从不正常终止,所以编译器会把 name1 的类型推断为非空,因为 name1 在为 null 时的分支处理会始终抛出异常

data class User(val name: String?)

fun fail(message: String): Nothing {
    throw IllegalStateException(message)
}

fun main(args: Array<String>) {
    val user = User("leavesC")
    val name = user.name ?: fail("no name")
    println(name) //leavesC

    val user1 = User(null)
    val name1 = user1.name ?: fail("no name")
    println(name1.length) //IllegalStateException
}
复制代码

四、函数

Kotlin 中的函数以关键字 fun 作为开头,函数名称紧随其后,再之后是用括号包裹起来的参数列表,如果函数有返回值,则再加上返回值类型,用一个冒号与参数列表隔开

//fun 用于表示声明一个函数,getNameLastChar 是函数名
        //空括号表示该函数无传入参数,Char 表示函数的返回值类型是字符
        fun getNameLastChar(): Char {
            return name.get(name.length - 1)
        }
复制代码
//带有两个不同类型的参数,一个是 String 类型,一个是 Int 类型
        //返回值为 Int 类型
        fun test1(str: String, int: Int): Int {
            return str.length + int
        }
复制代码

此外,表达式函数体的返回值类型可以省略,返回值类型可以自动推断。对于有返回值的代码块函数, 必须显式地 写出返回类型和 return 语句

//getNameLastChar 函数的返回值类型以及 return 关键字是可以省略的
        //返回值类型可以由编译器根据上下文进行推导
        //因此,函数可以简写为以下形式
        fun getNameLastChar() = name.get(name.length - 1)
复制代码

如果无返回值,则可以声明 Unit ,也可以省略 Unit

以下三种写法都是等价的

fun test(str: String, int: Int): Unit {
            println(str.length + int)
        }

        fun test(str: String, int: Int) {
            println(str.length + int)
        }

        fun test(str: String, int: Int) = println(str.length + int)

复制代码
原文  https://juejin.im/post/5cdd24265188250d8b2df425
正文到此结束
Loading...