目录
写在前面
之前的一段时间也学了一部分的Kotlin,但是都是断断续续的,感觉效果不太好,于是乎,就狠下心来,决定每天抽出一部分时间来系统的学习一下Kotlin这门语言,有Java基础相信不会太难吧!在进入正式的学习之前,先来对Kotlin做一个简单介绍:
Kotlin简介
1、Kotlin是一门可以运行在Java虚拟机、Android、浏览器上的静态语言,它与Java 100%兼容,Kotlin除了自己的标准库之外,大多仍然使用经典的Java集合框架。Kotlin V1.0于2016年2月15日发布,是第一个官方稳定版本,JetBrains从该版本开始长期向后兼容,在Google I/O 2017中,Google宣布Kotlin成为Android的官方开发语言。
2、入门心法之《Hello World》
一套从天而降的掌法自然是相当厉害啦,但是一口吃不成个胖子,除非你本来就胖,那我们还是得从基本功练起,先来看个入门内功心法Kotlin——Hello World!在写代码之前还得说两句,因为我们是在学语言,都是一些很枯燥的东西,所以一定得有耐心,再者就是这里使用的不是Android Studio这个开发工具,用的是它的老娘Intellij IDEA,而且JetBrains推出Kotlin有一个原因也是希望能够促进IDEA的销售,大家学语法的时候因为并没有使用在app中,所以最好使用这个工具,操作起来更方便,直接从控制台看结果。这个工具的下载大家可以到官网上下载,也可以去谷歌找破解版软件,使用方式熟悉as的应该都知道怎么用的,下面介绍一下如何创建一个基于gradle的Kotlin的项目,我就不再写详细的教程了,这个没啥说的,直接放个录屏大家看一下吧:
下面来先写个HelloWorld吧:
package com.jarchie.cn
fun main(args:Array<String>) {
println("Hello World!")
}
是不是发现跟Java的main函数有些类似,这样就可以在控制台输出一行Hello World了:
补充:目前在学习Kotlin的小伙伴绝大部分都是Android开发者,所以这里再多废话两句,如果你不想下载Intellij IDEA的话,直接使用Android Studio也是可以练习Kotlin的语法的,只不过你需要先创建一个Android工程,语言直接选择Kotlin就行,如果你选择的是Java,那么你需要自己配置Kotlin的插件,推荐依赖阿里云镜像仓库,这样构建时间可以大大缩短,然后直接创建.kt的文件就OK了,下面开始的学习是基于Kotlin 1.3.50的版本进行的:
一、基本类型
我们学过Java都知道哈,在Java中对于基本类型它其实是分两种的,既有基本类型还有一个包装类型,但是注意在Kotlin中就没有了,就只有一个基本类型:
Kotlin vs Java | ||
---|---|---|
Kotlin | Java | |
字节 | Byte | byte/Byte |
整型 | Short & Int & Long | short/Short & int/Integer & long/Long |
浮点型 | Float & Double | float/Float & double/Double |
字符 | Char | char/Character |
字符串 | String | String |
①、声明变量
举个栗子:
又由于Kotlin的编译器非常聪明,它会给咱们做类型推导,所以代码就变成了下面这个样子:
②、Boolean类型
任何事物都具有两面性,那这个Boolean类型就是只有两面性,它只有两种状态true和false,它的写法很简单:
//Boolean类型的基本写法
val B1 : Boolean = true
val B2 : Boolean = false
③、容易混淆的Long类型标记
Kotlin中定义Long类型必须使用大写L,这点不同于Java中的定义,需要注意
④、Kotlin的数值类型转换
可以看到直接转会报错,那该怎么办呢?嗯,官方推荐这样办:
④、无符号类型
这个玩意在Java里面是没有的哦,但是在Kotlin1.3开始有了,我们可以把它看做是为了兼容C而设计的吧,主要就是表示的数据范围变大了:
无符号类型(v1.3) | ||
有符号类型 | 无符号类型 | |
字节 | Byte | UByte |
短整型 | Short | UShort |
整型 | Int | UInt |
长整型 | Long | ULong |
字符串 | String | String |
⑤、字符类型
它是双字节16位的Unicode字符,它表示一个字符,对应Java中的Character,一个字符可以是一个字也可以是一个符号,字符用单引号''引起来,例如:'a'、'+','\n'等,来看代码:
val aChar: Char = '0'
val bChar: Char = '国'
//val cChar: Char = '\u000f' // \u表示的就是Unicode,后面的000f是一个Unicode编码
val cChar: Char = '\n' //换行符
fun main(args: Array<String>) {
println(aChar)
println(bChar)
println(cChar)
}
⑥、Kotlin的字符串
Kotlin中的字符串操作你可以就把它当做Java里面的去使用,基本上都是一致的,只有个别地方需要注意:
字符串比较
- a==b:比较内容,等价于Java的equals()方法
- a===b:比较对象是否是同一个对象
字符串模板
- "hello,$name"=>"hello,小王"
下面就对上面的内容来实际写点代码感受一下,因为大家都是由Java基础的,所以前期学习Kotlin最好的方式就是实际去敲一敲,前面并没有出现和Java中差别很大的需要仔细理解的概念:
fun main() {
val y: Boolean = true
val n: Boolean = false
println("布尔类型:$y$n")
var a = 2 //小技巧:搜索TypeInfo会提示快捷键可以查看变量的具体类型
val b = "Hello Kotlin"
// val c = 12345678910l //编译器报错
val c = 12345678910L //编译通过
val d = 3.0//Double
val e = 3.0f //Float
val f: Int = 10
// val g:Long = f //编译报错
val g: Long = f.toLong() //正常
val float1: Float = 4.1f
val double1 = float1.toDouble() //也需要转换
val h: UInt = 10u //无符号类型
val i: ULong = 100000000000000u
val str = "I Love China!"
println("Value of String 'str' is: $str")
println("Length of String 'str' is: ${str.length}")
//字符类型
val ch1: Char = 'L'
val ch2 = 'o'
val ch3 = 'v'
val ch4 = 'e'
println("字符类型:$ch1$ch2$ch3$ch4")
//字符串类型
val j = "Today is sunny day"
val k = String("$j".toCharArray()) //等同于Java中的 String k = new String("Today is sunny day");
val l = String("Today is sunny day".toCharArray()) //String()就是一个函数
println(j === k) //===比较引用,这里注意和Java的区别,Java中==比较的是引用,equals()比较的才是内容
println(j == k) //==比较内容
println(k === l)
println(k == l)
//Raw String,Java中写起来就很麻烦了,需要各种转义,看着很不直观
val m = """
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>乔布奇</title>
</head>
<body>
<p>This is my web page.</p>
</body>
</html>
""".trimIndent() //将公共字符串replace掉
println(m)
}
执行结果如下:
二、数组
数组对应英文单词Array,跟数没啥关系,主要是组,它是一系列对象:
Kotlin vs Java | ||
Kotlin | Java | |
整型 | IntArray | int[] |
整型装箱 | Array<Int> | Integer[] |
字符 | CharArray | char[] |
字符装箱 | Array<Char> | Character[] |
字符串 | Array<String> | String[] |
Kotlin中的数组,其实在Java中都有与之类似的,除了上表中的这些还有Float、Double。。。等等。
①、数组的创建
②、数组的长度
③、数组的读写
④、数组的遍历
或者
来吧,再对上面的内容做一个简单的练习:
fun main() {
val a = IntArray(5)
println(a.size)
val b = ArrayList<String>()
println(b.size)
val c0 = intArrayOf(1, 2, 3, 4, 5)
val c1 = IntArray(5) { it + 1 } //y=x+1,x就是下标index即0-4
println(c1.contentToString()) //提供函数方便打印数组内容
val d = arrayOf("Hello", "Kotlin")
d[1] = "Jarchie" //修改数组下标为1的位置上的元素的值
println("${d[0]},${d[1]}") //[hello,Jarchie]
val e = floatArrayOf(1f, 2f, 3f, 4f, 5f)
// for (element in e) { //遍历数组的第一种方式
// println(element)
// }
e.forEach { element -> println(element) } //遍历数组高阶函数的方式
//判断某个元素是否在数组中,Kotlin的写法就是这么简单,不用再像Java里面那样还要写循环了吧
if (1f in e){
println("1在e这个数组中")
}
if (1.1f !in e){
println("1.1不在e这个数组中")
}
}
执行结果如下:
三、区间
区间这个概念虽然Java中没有,但是我们在中学数学里面都是学过的,别跟我说你没学过我记得初中就有吧,所以理解起来还是很简单的:
- 一个数学上的概念,表示范围
- ClosedRange的子类,IntRange最常用
- 基本写法:0..100表示[0,100]; 0 until 100表示[0,100); i in 0..100判断i是否在区间[0,100]中
①、区间的创建
②、区间的迭代
或者:
③、区间的包含关系
④、区间的应用
下面还是通过一段代码来整体的巩固一下吧:
fun main() {
val intRange = 1..10 //[1,10] //闭区间,类型为intRange
val floatRange = 1f..10f //[1,10]
//它跟第一个的区别就是,上面的是一个个离散的点,下面的是一个个连续的值
val doubleRange = 1.0..10.0 //[1,10]
val charRange = 'a'..'z' //字符区间
val longRange = 1L..100L //长整型区间
//intRange是离散点,范围内的数值是可计数的,咱们可以调用函数打印区间内具体的值
println(intRange.joinToString())
//但是doubleRange是不可数的,它并没有提供该函数供我们打印,使用toString()也是不行的,打印出的就是描述信息
println(doubleRange.toString())
val uintRange = 1U..10U //类型是UIntRange,无符号类型
val ulongRange = 1UL..10UL
val intRangeWithStep = 1..10 step 2
val charRangeWithStep = 'a'..'z' step 2
val longRangeWithStep = 1L..100L step 10
println(intRangeWithStep.joinToString())
val intRangeExclusive = 1 until 10 //[1,10)左闭右开区间
val charRangeExclusive = 'a' until 'z'
val longRangeExclusive = 1L until 100L
println(intRangeExclusive.joinToString())
val intRangeReverse = 10 downTo 1 //倒序区间10-1
val charRangeReverse = 'z' downTo 'a'
val longRangeReverse = 100L downTo 1L
println(intRangeReverse.joinToString())
//区间的遍历
// for (ele in intRange) {
// println(ele)
// }
intRange.forEach {
println(it)
}
if (3 in intRange) {
println("3在intRange这个区间内")
}
if (3.0 in doubleRange) {
println("3在doubleRange这个区间内")
}
if (20 !in intRange) {
println("20不在intRange这个区间内")
}
//上一部分讲到数组遍历的时候你会发现没有像java中的int i=0;i++;i<array.length的这种写法
//学完这一部分咱们可以曲线救国,比如,这里举个例子
val array = intArrayOf(1, 3, 5, 7, 9)
// for (i in 0 until array.size) {
// println(array[i])
// }
for (i in array.indices) { //kotlin中将0 until array.size直接定义成了一个属性可以直接调用
println(array[i])
}
}
执行结果如下:
四、集合框架
- 增加了“不可变”集合框架的接口
- 复用Java API的所有实现类型
- 提供了丰富易用的方法,例如forEach/map/flatMap等高阶函数
- 运算符级别的支持,简化集合框架的访问
集合框架的接口类型对比 | ||
Kotlin | Java | |
不可变List | List<T> | List<T> |
可变List | MutableList<T> | |
不可变Map | Map<K,V> | Map<K,V> |
可变Map | MutableMap<K,V> | |
不可变Set | Set<T> | Set<T> |
可变Set | MutableSet<T> |
①、集合框架的创建
上面这几种方式是我们使用了Kotlin提供的函数来直接创建的,那有些朋友可能会说了,我不习惯这种方式,我就喜欢用new ArrarList()的方式创建,那该怎么办呢?那就直接这样写呗,和Java中没有太大的区别,就是不用写new关键字了:
可以看到它俩的写法基本上差不多,只是包名不一样,但是你要知道的是它俩本质上是一样的,我们来看看Kotlin是如何做到这俩玩意本质一样的?其实就是起了个别名,请往下看:
集合实现类复用与类型别名
②、集合框架的修改
Kotlin中集合框架可以直接使用运算符,也就是后面会说到的运算符重载,这里不作介绍,后面会说,这里我们就记住对于List来说,+=等价于add:
③、集合框架的读写
Pair:Kotlin标准库提供的数据类,表示二元组,键值对 a to b的这种写法
Triple:Kotlin标准库提供的数据类,表示三元组,三个元素
同样的,我们也来实际写点代码感受一下吧:
fun main() {
val intList: List<Int> = listOf(1, 2, 3, 4)
val intList2: MutableList<Int> = mutableListOf(1, 2, 3, 4)
println(intList)
println(intList2)
val map: Map<String, Any> = mapOf("name" to "Jarchie", "age" to 18)
val map2: Map<String, Any> = mutableMapOf("name" to "Jarchie", "age" to 18)
println(map)
println(map2)
val stringList = ArrayList<String>()
println(stringList)
for (i in 0..5) {
stringList.add("num:$i")
}
println(stringList)
for (i in 0..5) {
stringList += "num:$i"
}
println(stringList)
stringList[5] = "HelloKotlin"
val valueAt5 = stringList[5]
println(stringList)
val hashMap = HashMap<String, Int>()
hashMap["Hello"] = 10
println(hashMap["Hello"])
val pair = "Hello" to "Kotlin"
// val pair = Pair("Hello","Kotlin")
// val first = pair.first
// val second = pair.second
// val (x,y) = pair
val triple = Triple("张三", "男", 18)
val first = triple.first
val second = triple.second
val third = triple.third
val (x, y, z) = triple
println(triple)
}
执行结果如下:
五、函数
- 有自己的类型
- 可以赋值、传递,并在合适的条件下调用
①、函数的定义
②、函数 vs 方法
- 方法可以认为是函数的一种特殊类型
- 从形式上,有receiver的函数即为方法
举个栗子:下面的代码示例中,第一个称之为函数,第二个称之为方法,形式上很明显的看到方法的外面套了个类,它的receiver就是外面套的这个类的实例
//函数
fun apple(color:String,weight:Int):Any{
TODO()
}
class Food{
//方法
fun apple(color:String,weight:Int):Any{
TODO()
}
}
③、函数的类型
下图中左侧为函数,右侧就是函数类型:对于顶级函数来说就是这种形式:(参数类型)——> 返回值类型,对于方法来说它有点特殊,前面说了方法都是有个receiver,所以它的类型就变成了这种形式:receiver.(参数类型)——>返回值类型,本质上你可以把这个receiver当做函数的第一个参数来看,这点有点像python哈,python里面定义方法的时候它都有一个self,self作为第一个参数明确的放在那里,Java中就是把这一层给隐藏了,所以有点不太好理解,大家都会很自然的觉得this就应该能访问到,其实this是通过函数第一个参数传递进来的,只不过没有显示的声明而已
④、函数的引用
函数在传递的时候传递的实际上是它的引用,很多语言里面函数都是可以对外赋值的,但是在Java中没有这个概念,Kotlin里面现在有了,所以需要好好理解一下,如下图所示,左侧的是函数,右侧的这种奇怪的表达式就是函数的引用,对于函数来说是:冒号冒号函数名,对于方法来说是:receiver冒号冒号方法名
如何定义变量来接收这种引用值呢?
又因为编译器可以对类型做推断,所以可以简写成下面这种方式:
再来看当我们给Foo这个类实例化出一个对象的时候,它又不一样了,这玩意叫做 绑定receiver的函数引用,此时它的引用已经没有了那个receiver了,举个栗子看下面的图,一个初中数学的函数表达式f(r,x,y)=r*(x+y),r可以看作receiver,当我们给r赋特定值的时候,看函数的变形,其实到最后这个函数变的只跟x,y有关了,所以我们可以把它看做是另一个函数m(x,y),有点绕哈,先这样理解吧,后面咱们会通过代码来实际的感受一下:
⑤、变长参数
顾名思义:函数的参数个数在调用之前是不确定的
⑥、多返回值
多返回值就是函数返回值的个数是确定的,但是是多个
⑦、默认参数
注意,使用默认参数的话,要是参数列表中的最后一个参数,不是最后的参数编译器不知道你默认代替的是哪一个:
那如果我就想要不是最后一个参数那咋办呢?哼哼,使用具名参数:
接下来我们还是来看几个例子吧:
fun main() {
//x1,x2,x3三种不同的方式,但本质上是一样的
val x1: (Foo, String, Long) -> Any = Foo::bar
val x2: Foo.(String, Long) -> Any = Foo::bar
val x3: Function3<Foo, String, Long, Any> = Foo::bar
println(x1)
val y: (Foo, String, Long) -> Any = x1 //将x赋值给另一个变量
yy(x1) //将x作为参数传递到一个函数里面
val f: () -> Unit = ::foo
val g: (Int) -> String = ::foo
val h: (Foo, String, Long) -> Any = Foo::bar
println(foo(5))
//变长参数
multiParameters(1, 2, 3, 4)
//具名参数
defaultParameter(y = "Hello")
//多返回值
val (a, b, c) = multiReturnValues()
val r = a + b
val r1 = a + c
println("r:$r r1:$r1")
}
fun yy(p: (Foo, String, Long) -> Any) {
// p(Foo(), "Hello", 3L)
// p.invoke(Foo(),"Hello",3L)
}
class Foo {
fun bar(p0: String, p1: Long): Any {
return "111"
}
}
fun foo() {}
fun foo(p0: Int): String {
return "P0:$p0"
}
fun defaultParameter(x: Int = 5, y: String, z: Long = 0L) {
println("x:$x,y:$y,z:$z")
}
fun multiParameters(vararg ints: Int) {
println(ints.contentToString())
}
fun multiReturnValues(): Triple<Int, Long, Double> {
return Triple(1, 3L, 4.0)
}
执行结果如下:
六、综合案例
这一部分我们将上面讲的这些内容做一个综合性的应用,来开发一个简易版的命令行计算器:
/**
* 作者:created by Jarchie
* 时间:2020/7/14 14:26:12
* 邮箱:[email protected]
* 说明:Kotlin内置类型综合案例:简易四则计算器
*/
fun main() {
println("-----------欢迎使用命令行计算器------------")
while (true) {
println("请输入算式例如:1 + 1")
val input = readLine() ?: break
val splits = input.trim().split(" ")
if (splits.size < 3) {
showHelp()
break
}
val operators = mapOf(
"+" to ::plus,
"-" to ::minus,
"*" to ::times,
"/" to ::div
)
val arg1 = splits[0]
val op = splits[1]
val arg2 = splits[2]
val opFunc = operators[op] ?: return showHelp()
try {
println("$input = ${opFunc(arg1.toInt(), arg2.toInt())}")
} catch (e: Exception) {
println(e.message)
showHelp()
}
println("输入[Y]重新计算:")
val cmd = readLine()
if (cmd == null || cmd.toLowerCase() != "y") {
break
}
}
}
//加法
fun plus(arg0: Int, arg1: Int): Int {
return arg0 + arg1
}
//减法
fun minus(arg0: Int, arg1: Int): Int {
return arg0 - arg1
}
//乘法
fun times(arg0: Int, arg1: Int): Int {
return arg0 * arg1
}
//除法
fun div(arg0: Int, arg1: Int): Int {
return arg0 / arg1
}
//帮助信息
fun showHelp() {
println(
"""
计算示例:
输入: 3 * 4
输出: 3 * 4 = 12
""".trimIndent()
)
}
执行结果如下:
以上就是关于Kotlin的内置类型的所有内容了,相信你看完这些已经可以轻松入门Kotlin开发了!嗯,加油哦!
今天的内容就这么多,下期再会!
祝:工作顺利!