Kotlin快速入门(一)——基础类型

最近了解一下Kotlin,毕竟Google力挺,网上对于Kotlin褒贬不一,有吹捧的,也有贬低的,在拿不定主意的时,不如自己动手试试,看看Kotlin会带来哪些新的体验,本文主要针对Java的开发者,着重总结两者区别。

Kotlin的优势

好好了解一下,不然被别人问为什么使用Kotlin,会被问的哑口无言。

  • 全面支持Lambda表达式
  • 数据类(Data classes)
  • 函数字面量和内联函数(Function literals & inline functions)
  • 函数扩展(Extension function)
  • 空安全(Null safety)
  • 智能转换(Smart casts)
  • 字符串模版(String templates)
  • 主构造函数(Primary constructors)
  • 类委托(Class delegation)
  • 类型判断(Type inference)
  • 单例(Singletons)
  • 声明点变量(Declaration-site variance)
  • 区间表达式 (Range expressions)

Kotlin的基础类型

1. 分隔符

Kotlin每行语句可以不以分号结束,如果一行写多条独立的语句时需要使用分号。

fun main(args: Array<String>) {
    println("Hello World")
    println("Hello");println("World")
}

2. 注释

多行注释可以嵌套使用;单行注释和文档注释与Java一致。

fun main(args: Array<String>) {

    /*println("Hello world")

    /*println("first")*/

    println("second")*/

    println("third")
}

简书的 Kotlin 代码高亮有点问题,放到编译器中的效果是正常的。

注释嵌套

3. 变量

  • 声明变量varvalval是只读数据类型,只能赋值一次
  • 类型推断,可以不显式声明变量类型
fun main(args: Array<String>) {

    var i: Int // 显式声明变量i的类型,Java中的int a;
    var name: String = "Kotlin" // 显式声明变量并赋值,Java中的String name = "Kotlin";
    var my_name = "JamFF" // 上面的简写,编译器推断Sting类型

    name = "Java" // 重新赋值,Java中的name = "Java";
    my_name = 18 // my_name是String类型,不能赋值为Int

    val a = false // 不可以重新赋值,Java中的final boolean b = false;

    val b: Int // 不同于Java,可以先声明,不赋值
    b = 1 // 只要在使用之前赋值即可,只能赋值一次
}

4. 整型

  • Byte,Java的byteByte,8bit。
  • Short,Java的shortShort,16bit。
  • Int,Java的intInteger,32bit。
  • Long,Java的longLong,64bit。
  1. 整型会默认类型推断为Int;如果超过Int取值范围,会推断为Long;如果需要定义为Long类型,除了显式声明数据类型外,还可以在结尾处使用大写的L来表示,而在 Java 大小写的L均可。

    fun main(args: Array<String>) {
    
        var a: Int = 18
        a = 100_000_000_000 // 不能赋值,超过Int范围
    
        var b = 1// 整型默认推断为Int,Byte,Short需要显式声明数据类型
        // var b: Byte = 1
        // var b: Short = 1
        // var b = 1L// Long可以不声明数据类型,在尾部使用L表示
        var c = 100_000_000_000 // 如果超过Int范围,会自动推断类型为Long
    }
    
  2. Kotlin是空安全的语言,所以上述四种数据类型均不能接受null值,如果要存储null值需要使用Byte?Short?Int?Long?类型。

    fun main(args: Array<String>) {
    
        val a: Int = null // 报错
        val b: Int? = null // 正确
    }
    
  3. Kotlin将不带?类型映射为Java的基本类型,将带?类型映射为Java的引用类型。

    fun main(args: Array<String>) {
    
        val a: Int = 666
        val b: Int = 666
        // ===比较的是地址,==比较的是值
        println(a === b) // true,基本类型比较
    
        val c: Int? = 666
        val d: Int? = 666
        println(c === d) // false,引用类型比较
    }
    
  4. Kotlin支持二进制、十进制和十六进制,不支持八进制。

    fun main(args: Array<String>) {
    
        val a = 0b10101
        val b = 0xab
        // a的值为21,b的值为171,a+b的值为192
        println("a的值为$a,b的值为$b,a+b的值为${a + b}")
    }
    

5. 浮点型

与Java一致。

  • Float:32bit,需要在尾部添加Ff
  • Double:64bit,类型推断的默认值。
fun main(args: Array<String>) {

    var a = 3.14 // 默认Double
    a = 2.0f // 报错,不能赋值给Float
    var b = 1.5f // Float
    b = 3.14 // 报错,不能赋值给Double
}

6. 字符型

与Java不同,Kotlin的Char型变量不能当成整数值使用。

fun main(args: Array<String>) {

    var a: Char = 'a' // 字符a
    a = '\n' // 换行符
    a = '\u8888' // 汉字'驾'
    a = 1 // 报错
}

7. 数值型之间的类型转换

  1. Kotlin不支持取值范围小的数据类型隐式转换为取值范围大的类型,需要显式调用。

    • toByte()
    • toShort()
    • toInt()
    • toLong()
    • toFloat()
    • toDouble()
    • toChar()
    fun main(args: Array<String>) {
    
        var a: Byte = 1
        var b: Short = 2
    
        b = a // 报错,Java允许
        a = b.toByte() // 可以,注意溢出
        b = a.toShort() // 可以
    
        var c = 1.0f
        var d = 2.2
    
        d = c // 报错,Java允许
        c = d.toFloat() // 可以,注意溢出
        d = c.toDouble() // 可以
    }
    
  2. 与Java一致,虽然Kotlin中缺乏隐式转换,但在表达式中可以自动转换。通过javaclass查看数据类型。

    fun main(args: Array<String>) {
    
        val a: Byte = 1
        val b: Short = 2
        val c = 1.0
    
        val total = a + b
        // javaClass属性来自Any类型,是Kotlin所有类型但根父类
        println(total.javaClass) // int
    
        val total2 = a.toLong() + b.toByte()
        println(total2.javaClass) // long
    
        val total3 = a + b + c
        println(total3.javaClass) // double
    }
    
  3. 与Java一致,将浮点型强转为整数型,小数部分会被截断;将整数型强转为浮点型没有问题。

  4. Kotlin中Char型虽然不能当成整数进行算术运算,但是可以Char型值加、减一个整数值,也可以两个Char相减,但不能相加。

    fun main(args: Array<String>) {
    
        val a = 'a'
        val b = 'b'
    
        println(a + 2) // c
        println(b + 2) // d
        println(b - a) // 1
        println(a + b) // 报错
    }
    

8. Boolean类型

与Java一致。

9. 空安全

  1. 非空类型和可空类型

    fun main(args: Array<String>) {
    
        val str = "abc"
    
        val num: Int = str.toIntOrNull() // 报错
        val num2: Int? = str.toIntOrNull() // 通过
        val num3 = str.toIntOrNull() // 通过,类型推断为Int?类型
    }
    

    那么Kotlin是如何保证空安全呢

    fun main(args: Array<String>) {
    
        val a: String = "abc"
        val b: String? = "abc"
    
        println(a.length) // a非null,不可能出现空指针
        println(b.length) // b可以为null,但是如果不判断非空,编译报错
    }
    
  2. 先判断后使用
    可空类型的变量不允许直接调用方法或属性,必须判断是否为null。

    fun main(args: Array<String>) {
        
        val a: String? = "abc"
        // 可以使用安全调用和Elvis简化
        val len = if (a != null) a.length else -1
        
        if (a != null && a.isNotEmpty()) {
            println(a.length)
        } else {
            println("空字符串")
        }
    }
    
  3. 安全调用

    • 可以使用?.进行安全调用,避免出现空指针。
    fun main(args: Array<String>) {
    
        var a: String? = "abc"
    
        println(a?.length) // 打印3
    
        a = null
    
        println(a?.length) // 打印null
    }
    
    • 与Spring EL类似,Kotlin的安全调用也支持链式调用。
    fun main(args: Array<String>) {
    
        // 安全的获取user的dog的name,如果user或者user.dog为null,整个表达式返回null
        user?.dog?.name
    }
    
    • 安全调用还可与let全局函数结合使用,例如下面只打印非空元素。
    fun main(args: Array<String>) {
    
        val arr: Array<String?> = arrayOf("abc", "JamFF", "Tom", null, "Tony")
        for (s in arr) {
            s?.let { println(it) }
        }
    }
    
  4. Elvis运算
    ?:运算符就是Elvis,如果?:左边不为null返回左边表达式的值,否则返回?:右边表达式的值。

    fun main(args: Array<String>) {
        
        val a: String? = "abc"
        
        val len = if (a != null) a.length else -1
        
        val len2 = a?.length ?: -1 // 上面语句的简写
        
        if (a != null && a.isNotEmpty()) {
            println(a.length)
        } else {
            println("空字符串")
        }
    }
    

    Kotlin的returnthrow都属于表达式,可以灵活使用。

  5. 强制调用
    !!.不管变量是否为null强制调用,可以编译通过,运行可能引发空指针,谨慎使用。

    fun main(args: Array<String>) {
    
        var a: String? = "abc"
        println(a!!.length) // 输出3
    
        a = null
        println(a!!.length) // 空指针
    
        val arr: Array<String?> = arrayOf("abc", "JamFF", "Tom", null, "Tony")
        for (s in arr) {
            s!!.let { println(it) } // 空指针
        }
    }
    

10. 字符串

  1. 遍历字符串中每一个字符

    fun main(args: Array<String>) {
    
        val str = "JamFF"
    
        for (c in str){
            println(c)
        }
    }
    
  2. 字符串分类

    • 转义字符串,可以包含转义字符,类似Java中的字符串。
    • 原始字符串,可以包含换行符和任意文本,需要三个引号包裹。
    fun main(args: Array<String>) {
    
        val str = "abc" // 转移字符串
    
        // 原始字符串
        val txt = """
            但行好事,
            勿问前程。
        """
    
        println(txt)
    
        // trimIndent去除字符串前面的缩进
        val txt2 = """
            但行好事,
            勿问前程。
        """.trimIndent()
        println(txt2)
    
        val txt3 = """
            |但行好事,
            |勿问前程。
        """
        println(txt3)
    
        // trimMargin去除边界符,Kotlin默认是"|",
        val txt4 = """
            |但行好事,
            |勿问前程。
        """.trimMargin()
        println(txt4)
    
        // 去除自定义边界符
        val txt5 = """
            ^但行好事,
            ^勿问前程。
        """.trimMargin("^")
        println(txt5)
    }
    

    输出结果

    输出结果
  3. 字符串模版
    Kotlin允许在字符串(转移字符串、原始字符串)中嵌入变量或表达式,只要放入${}中即可。

    fun main(args: Array<String>) {
    
        val a = 2018
        var s = "今年是${a}年"
        println(s)
    
        s = "随机数:${java.util.Random().nextInt(10)}"
        println(s)
    }
    
  4. Kotlin字符串的方法
    Kotlin的String和Java的不是同一个类,有更多便捷的API

11. 类型别名

  • 类似于C语言的typedef的功能,Kotlin可以使用typealias定义类型别名。
    fun main(args: Array<String>) {
    
        // Kotlin: Nested and local type aliases are not supported
        typealias StringSet = Set<String> // 报错,别名不能写在方法中
        val set: StringSet
        var table: FileTable<String>
    }
    // 正确定义位置
    typealias StringSet = Set<String>
    typealias FileTable<K> = MutableMap<K, MutableList<File>>
    
  • 也可以给内部类定义别名。
    class A {
        inner class Inner
    }
    
    class B {
        inner class Inner
    }
    
    typealias AInner = A.Inner
    typealias BInner = B.Inner
    
    fun main(args: Array<String>) {
        val a: AInner = A().Inner()
        val b = B().Inner()
    
        println(a.javaClass) // 输出class A$Inner
        println(b.javaClass) // 输出class B$Inner
    }
    
  • Kotlin的Lambda表达式的类型直接就是函数类型,而Java的是函数是接口,因此Kotlin也允许为Lambda表达式的类型指定别名。
    // 为(T) -> Boolean类型指定别名Predicate<T>
    typealias Predicate<T> = (T) -> Boolean
    
    fun main(args: Array<String>) {
    
        // 使用Predicate<String>定义变量,该变量的值是一个Lambda表达式
        val p: Predicate<String> = { it.length > 4 }
        // 为filter()方法传入p参数,只保留长度大于4的字符串
        println(arrayOf("Java", "PHP", "Python", "Go", "Kotlin").filter(p))
        // 输出[Python, Kotlin]
    }
    

重点

  • 每行语句建议不以分号结束,换行即可。
  • 可变变量var和不可变变量val
  • 基本数据类型
  • 空安全
  • 字符串和字符串模版
  • 类型别名
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容