入门Kotlin,认识一下基本类型与容器...

【Kotlin】初识Kotlin(一)

1、浅短认识Kotlin

首先,咱们只需要认识一点,就可以看见这门语言的含金量!

在Google I/O 2017中,Google 宣布 Kotlin 成为 Android 官方开发语言。

其次,我们在认识到一点,就可以看见这门语言的兼容性!

Kotlin能够和Java达到100%互通,也就是说,使用Kotlin依旧可以调用 Java已有的代码或库,也可以同时使用Java和Kotlin来混合编写代码。

最后,Kotlin中的很多语法的规范性和安全性都得到了质的提高,同时,其语法的难易度也下降了很多,学习过Java的基本上入门Kotlin也是非常友好的!

我对Kotlin初步的看法是什么呢?

  • 代码简短,很多语法糖有python的影子,格式也类似
  • 思想典型,面向对象的思想基本和Java可以一一对应
  • 美观易懂,在lambda上的精简程度可以说是完美
  • ......

Kotlin有很多优点,而学习它能过在未来的编程过程中打下坚实的基础!

2、如何创建Kotlin项目?

无论是IDEA还是Android Studio,都是可以直接编辑Kotlin代码的。

  • 目前Spring boot框架已经兼容了Kotlin,并且可以创建Kotlin项目
  • Android开发也可以使用Kotlin来进行app的编写

我这里使用IDEA来举例子:

  1. 新建一个项目

    image-20220221165939578

  2. 创建Kotlin项目

    image-20220221170048366

  3. 一路next就完事了

  4. 最后需要等待gradle的第一次构建(需要比较长的时间,耐心等待一下)

3、hello world

使用Kotlin简简单单的写一个hello world

fun main(args: Array<String>) {
    println("hello world!")
}

上面的方法和Java中的如下代码是一样的

public class test {
    public static void main(String[] args) {
        System.out.println("hello world!");
    }
}

和Java的区别:

  • 不必用分号结束
  • 参数使用参数名: 参数类型的方式
  • main方法不需要写在一个class中

4、Kotlin控制台的输入输出

入门一门语言,最开始学习都是这样的流程

  • 输出 “输入姓名”
  • 通过控制台输入...
  • 输出 “你的姓名是XXX”

很多人都喜欢使用如上的方法来接触一门语言中最基本的方法(函数),如果想利用Kotlin来进行控制台交互,那么可以如此:

  • 使用println()或者print()来进行输出
  • 使用readLine()来进行控制台输入
  • 导入Java的Scanner来进行控制台输入

我们来演示一个最简单的例子

fun main() {
    println("请输入你的姓名:")
    val name = readLine()
    println("请输入你的年龄:")
    val scanner = Scanner(System.`in`)
    val age = scanner.nextInt()
    println("你的姓名是:$name")
    println("你的年龄是:$age")
}

/*
    请输入你的姓名:
    woodwhale
    请输入你的年龄:
    19
    你的姓名是:woodwhale
    你的年龄是:19

    进程已结束,退出代码0
*/

4、Kotlin中的变量与常量

1.可读写变量var

可读写变量在Kotlin中使用关键字var来表示,我们可以理解为一个具有getter和setter的private属性对象

格式就是: var 变量名:变量类型 = 赋值

var value:Int = 1

当然,kotiln很智能,可以推断出当前这个变量是什么类型的,所以上面的赋值我们可以简写为:

var value = 1

上面这个情况是整数,而我们知道,除了int类型,大部分语言中还有其他的数字数据类型,Kotlin中有如下几种基本的数据类型:

  • 整型
    • Short(16位)
    • Int(32位)(默认推断)
    • Long(64位)
  • 浮点型
    • Float(32位)
    • Double(64位)
  • 字节
    • Byte(8位)
  • 字符型
    • Char
    • String
  • 布尔型
    • Boolean

注意:

  • 不同的数据类型可以通过toXX()方法来转换,并且Kotlin不会Java一样,自动装箱拆箱,无法直接将一个Int类型值转为Long类型,需要调用toLong()方法来转换
  • 从较大的类型转换为较小的类型可能会截断该值,例如Long转为Byte

将上述的各个变量都使用Kotlin写一遍

fun main() {
    // 整型
    var oneShort:Short = 1
    var twoInt:Int = 1
    var threeLong:Long = 1
    // 浮点型
    var fourFloat:Float = 4.0f
    var fiveDouble:Double = 5.0
    // 字节
    var sixByte:Byte = 6
    // 字符
    var sevenChar:Char = '7'
    var eightString:String = "8"
    // 布尔型
    var flag:Boolean = true;
}

值得一提的是String变量使用了python中的三个引号的方式来声明变量,可以支持换行

fun main() {
    var longString = """
        oneLine
        twoLine
        threeLine
    """.trimIndent()
    println("my input : \n$longString")
}

如果上述代码没有trimIndent()方法,那么连tab缩减也会一并打印出来

细心的你还发现了一个$符号,其实这是Kotlin的一个特性,可以用$来指代变量(或者常量)

如果你想仅仅打出$这个符号,只需要加一个转义字符\到前面就可以了\$

println("\$longString")	// 这样输出的就是 $longString

2.可读不可写变量val

可读但是不可写的变量使用关键字val来修饰,我们可以把它理解成一个具有getter但是没setter的private对象

也就是说,val修饰的变量是只能读的,一旦申明就无法更改了

值得注意的是,Kotlin官方建议:能用val处理的变量尽量不要用var来处理

这里就不多细讲了,就是上述的var没有第二次更改的权限的一种变量

3.编译器常量const val

使用const val修饰的是常量,是在编译时期就会进行初始化的常量,我们可以理解为Java中public static final修饰的一个值

一般我们使用大写的字母加上下划线来对常量赋值,并且Kotlin中常量的赋值位置有两个

  1. top-level级别

    • 所谓的top-level就是位于代码文件的最外部,比如常见的类(非内部类和嵌套类)就是在top-level。意思是在结构上常量不属于任何的类,而是属于文件。
  2. object级别

    • object中可以指的是最外部的object也可以指的是companion object(伴生类)
// top-level
const val BASE_URL = "https://www.woodwhale.top/"

class Test {
    companion object {
        const val BASE_URL = "https://www.woodwhale.top/"
    }
}

现在还没学习到Kotlin的面向对象,所以不需要深入理解类与对象的思想

5、Kotlin中的区间和过程

区间在每个语言中都有,从最基础的for循环开始,就有区间的涉及了

Kotlin中对于区间有专门的数据对象IntRange,有专门的过程数据对象IntProgression

fun rangeFun() {
    val firstRange:IntRange = 0..1
    val secondRange = 0.rangeTo(1)
    val thirdRange = 0 until 2
    val fourthRange = 0.until(2)
}
fun progressionFun() {
    val firstProgression : IntProgression= 0 .. 100  step 2
    val secondProgression = 0 until 100 step 4
}

如上述代码,第一个和第二个区间是等价的,表示[0,1],第三个和第四个区间是等价的,表示[0,2)

当然IntRange类型和IntProgression类型可以省略不写,让Kotlin自动识别

我们一般使用区间和for循环一起使用

for (i in firstRange) {
    println(i)
}
for (i in firstProgression) {
    println(i)
}

6、Kotlin中的数据容器

数据容器最常用的就几种

  1. 数组
  2. set
  3. map
  4. list

后三种可以统称为集合

让我们来学习Kotlin中这些数据容器的基本使用方法!

1.数组

Kotlin中创建数组的方法有很多

  • arrayOf()方法
  • arrayOfNulls()方法
  • 动态创建数组

(1) arrayOf方法创建数组

创建一个数组并且将元素初始化,数组中的元素可以是任何类型

fun main() {
    val testArray = arrayOf(1,2,3)
    val testArray2 = arrayOf("String",'c',114,false)
}

(2) arrayOfNulls方法创建数组

创建一个指定大小,元素为空的数组,必须指定元素类型

fun main() {
    val testArrayOfNulls = arrayOfNulls<String>(114)
    testArrayOfNulls[0] = "first"
    testArrayOfNulls[113] = "last"
}

(3) 动态创建数组

我们可以使用Array()类的构造方法进行动态数组的实例化,需要初始化Array对象的大小,这里使用的是10,同时可以给其中的对象使用lambda进行赋值,如下代码就是将0~9这些数据存入到了Array中

fun main() {
    // 创建一个Array<Int>初始化为[0,1,2,3,4,5,6,7,8,9]
    val testArray = Array(10){i -> i}	// 使用lambda表达式
    println(testArray.joinToString())
}

/*
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    进程已结束,退出代码0
*/

(4) 原生类型数组

在Kotlin中有无装箱开销的专门的类来表示原生数组

原生数组类型含义
ByteArray字节数组
ShortArray短整型数组
IntArray整型数组
LongArray长整型数组
BooleanArray布尔型数组
CharArray字符型数组
FloatArray浮点型数组
DoubleArray双精度浮点数组

举个例子:

fun main() {
    // 初始化一个[0,1,2,3,4]的Int数组
    val testIntArray = IntArray(5){it}
    println(testIntArray.joinToString())
}

这里的it其实是lambda表达式中的内容,我们现在就理解为遍历这个数组时候的下标就可以了,将下标这个数字赋值给当前下标的数组内容,就可以得到[0,1,2,3,4]的Int数组啦

2.数组的遍历

我们尝试使用for循环进行数组的遍历,结合我们上面所学的区间和过程来完成

for循环有两种方式

  • 普通的for循环
  • foreach循环

在Kotlin中,也是具有这两种循环的方式,并且结合了Java中Array.steam的优点,也具有foreach循环的方法

下面我们就来学习一下两种循环如何写!

(1) for循环下标遍历

Java中,我们最常见的写法如下

for (int i = 0; i < someArray.length; i++) {
    doSomething()
}

很明显,这是在遍历一次数组,在``Kotlin`中可以结合区间来写

// 初始化一个[0,1,2,3,4]的Int数组
val testIntArray = IntArray(5){it*2}
for (index in 0 until  testIntArray.size) {
    println(testIntArray[index])
}

0 until testIntArray.size表示[0,testIntArray.size)这个区间,是不是和Java中的代码对应了呢?

但是Kotlin其实有更简单的遍历表达方式,已经帮你想好啦

// 初始化一个[0,1,2,3,4]的Int数组
val testIntArray = IntArray(5){it*2}
for (index in testIntArray.indices) {
    println(testIntArray[index])
}

使用testIntArray.indices属性,就可以得到当前这个数组的步频为1的区间过程啦,减少了代码量

(2) foreach元素遍历

foreach是进行元素遍历的,在Java中,这样的遍历如下:

for (String s in strs) {
    doSomething(s);
}

那么在kotln中,写法与Java类似,只不过方便了不用再写一次数据类型,Kotlin会自动推断

// 初始化一个[0,1,2,3,4]的Int数组
val testIntArray = IntArray(5){it*2}
for (intItem in testIntArray) {
    println(intItem)
}

当然了,我们不用担心存在不同元素类型的数组,我们在遍历的时候,都是可以对这些元素进行操作的

fun main() {
    val arr = arrayOf(1,"2",false,'4')
    for (item in arr) {
        when (item) {
            is Int -> println("Int type $item")
            is String -> println("String type $item")
            is Boolean -> println("Boolean type $item")
            is Char -> println("Char type $item")
        }
    }
}

/*
    Int type 1
    String type 2
    Boolean type false
    Char type 4

    进程已结束,退出代码0
*/

上述代码使用了when关键字,等到我们下一章再开始学,我们先理解其为switch就可以啦。并且,使用的is关键字表示判断item是否是某个数据类型。

在Kotlin中,还有一种forEach()方法,可以进行循环使用

arr.forEach {
    when (val item = it) {
        is Int -> println("Int type $item")
        is String -> println("String type $item")
        is Boolean -> println("Boolean type $item")
        is Char -> println("Char type $item")
    }
}

如上代码的效果与我们使用经典的forEach的效果是一样的,这里的it是迭代器中当前的item

(3)foreach带上下标

Kotlin中有一种特殊的foreach,可以将index和item同时使用,减少代码量

使用arr.withIndex()的方法,可以带出index和item两个值,就可以同时对这两个元素进行操作

val arr = arrayOf(1,"2",false,'4')
for ( (index,item) in arr.withIndex()) {
    println("$item 's index == $index")
}
/*
    1 's index == 0
    2 's index == 1
    false 's index == 2
    4 's index == 3

    进程已结束,退出代码0
*/

同时在Kotlin中,还有一种arr.forEachIndexed()的方法,也可以做到index和item同时更改

arr.forEachIndexed() {
    index, item ->
    println("$item 's index == $index")
}

或者将代码写在括号内也是可以的

arr.forEachIndexed({ index, item -> println("$item 's index == $index") }) 

这些方法都是使用了lambda表达式,现在看看用法就可以了,我们之后再详细学习!

3.集合

集合这种数据结构在各种工程中是必不可少的一部分。与数组不同的是可变集合的大小是可以动态改变的

  • List:有序集合,可以通过下标访问元素,同一元素可以在list中出现多次
  • Set:唯一元素集合,一组无重复元素的集合,一般不注重顺序,注重是否存在
  • Map:键值对集合,键唯一,值可以重复,一个映射,注重一一对应

与Java中的集合不同的是,Kotlin中的集合有可变的和不可变的,可变的集合可以动态添加元素,不可变的集合只能在初始化的时候就进行申明

(1) List的创建

我们先来创建一个可变集合,可以使用两种方法

  • arrayListOf<T>()
  • mutableListOf<T>()
fun main() {
    val mutableListOf = mutableListOf<String>("-1","0")
    mutableListOf.add("1")
    mutableListOf.add("2")
    mutableListOf.forEach() { println(it)}

    val arrayListOf = arrayListOf<String>()
    arrayListOf.add("3")
    arrayListOf.add("4")
    for (s in arrayListOf) { println(s)}
}

当前阶段,这两个方法返回的都是ArrayList,但是Kotlin官方计划将之后的mutableList与ArrayList区分开来。所以在Kotlin中咱们尽量使用mutableListOf()方法来创建list吧!

再来创建一个不可变的集合,使用方法

  • listOf<T>()
val listOf = listOf<String>("1", "2", "3")
println(listOf)

注意,listOf得到的集合是不可变的,也就是没有set方法,只能get!

(2) Map的创建

首先介绍Kotlin中的键值对,可以使用Pair("key", "value")的方式来创建,也可以简写成"key" to "value"

可变Map的创建也有很多方法

  • mutableMapOf<K,V>() (返回可变的mutableMap)
  • hashMapOf() (返回可变的HashMap)
  • linkedMapOf() (返回可变的LinkedHashMap)
  • sortedMapOf() (返回可变的TreeMap)

可变的map可以使用put方法动态添加,用get方法获取。当然也可以使用Kotlin自己的方式,使用下标来获取、赋值

fun main() {
    val mutableMap = mutableMapOf("key" to "value")
    mutableMap.put("key1","value1")
    mutableMap["k2"] = "v2"
    val hash = hashMapOf<String, String>("name" to "alfred")
    val linkedMap = linkedMapOf<String, String>("age" to "27")
    val treeMap = sortedMapOf("sex" to "man")

    println(mutableMap.get("key1"))
    println(hash["name"])
    println(linkedMap)
    println(treeMap)
}

那么如何删除map的key呢

  • 使用remove()方法
  • 或者使用 -
fun main() {
    val mutableMap = mutableMapOf("key" to "value")
    mutableMap.put("key1","value1")
    mutableMap["k2"] = "v2"
    mutableMap.remove("k2")
    mutableMap -= "key1"
    
    println(mutableMap)
}

不可变的Map创建的方式如下,使用mapOf<K,V>()方法

val mapOf = mapOf<String,String>(Pair("key", "value"))
val mapOf1 = mapOf("key" to "value")

需要注意的是,用mapOf创建的map是不可变的,所以需要在初始化的时候就申明key和value。

(3) Set的创建

和上面的集合结构一样,也是具有可变set和不可变的set

可变set的创建方式,与map类似

  • mutableSetOf<K,V>() (返回可变的mutableSet)
  • hashSetOf() (返回可变的HashSet)
  • linkedSetOf() (返回可变的LinkedHashSet)
  • sortedSetOf() (返回可变的TreeSet)
fun main() {
    val mutableSetOf = mutableSetOf("1", "2")
    mutableSetOf.remove("1")
    mutableSetOf -= "2"
    println("1" in mutableSetOf)
    println("3" in mutableSetOf)
}

不可变的set创建方式就是 setOf()方法

fun main() {
    val of = setOf("1", "2")
    println(of)
}

(4) 集合的总结

  1. listOf 创建不可变 List, mutableListOf() 创建可变的 List
  2. setOf 创建不可变 Set, mutableSetOf() 创建可变 Set
  3. mapOf 创建不可变 Map, mutableMapOf() 创建可变的 Map
  4. mutable 前缀的函数创建是可变的集合,没有 mutable 前缀的创建不可变的集合
  5. 不可变集合可通过 toMutable*() 函数换成可变的集合,也就是创建新的可变集合并把数据放进去

7、Kotlin的更多内容

由于篇幅限制,后面更多的Kotlin语法、关键字的使用方法在下一篇文章中讲述!