Kotlin 学习笔记 第 1 篇

背景

Kotlin 是 JetBrains 在 2011 年推出的一门全新编程语言,最早被设计运行在 JVM 上 , 使用 Kotlin 编写的程序会被编译成 Java 的字节码文件。Kotlin 可以和现在的 Java 语言包 100% 兼容, 而且 Kotlin 代码比 Java 代码更简洁、更富有表现力。它利用了 Java 的优势又比 Java 更简洁,此外 Kotlin 还可以被编译成 JavaScript 代码。 并且 Kotlin 已经被 Google 推荐为 Android 官方开发语言,这意味着 Kotlin 将会在 Android 开发中大放异彩。越来越多的 Android 应用正在转向 Kotlin ,因此我们也不能掉队哦。

1.Kotlin 的优势

1.1 用于服务端开发

Kotlin 程序可以被编译成 Java 字节码文件,字节码文件可以直接在 JVM 上运行,因此 Kotlin 非常适合开发后端应用程序。Kotlin 能与现有的 Java 语言包保持完全兼容,这也意味着 Kotlin 不是一门简单的语言,它完全可以利用 Java 领域中的各种技术框架,比如说: Spring 、 Hibernate、MyBatis、Lucene 等,因此 Java 开发者非常容易过渡到 Kotlin。

  • 简洁性:Kotlin 具有大量现代编程语言的简洁性和便捷性,被称作是 Android 平台的 Swift;
  • 兼容性:Kotlin 完全兼容 Java ,因此 Kotlin 可以自由使用 Java 领域的各种框架和库。因此,开发者可以保持熟悉的技术栈,又可以获得现代编程语言的优势;
  • 迁移性:Kotlin 支持大型项目从 Java 向 Kotlin 逐步迁移 —— 项目主体部分继续使用 Java,新开发的功能可以使用 Kotlin,也可以逐步使用 Kotlin 代替部分老旧的 Java 代码。
    短期内 Kotlin 不会对 Java 造成巨大的冲击,但是 Kotlin 简洁、优雅的语法可以对 Java 形成良好的补充,开发者可根据需要自由选择 Java 或者 Kotlin,最终都会生成字节码文件,运行在 JVM 上。

1.2 用于 JavaScript

Kotlin 可以编译成 JavaScript 代码, Kotlin 程序会生成遵循 ECMAScript 规范的 JavaScript 代码。 当选择生成 JavaScript 目标是,不近会包括开发着自己写的 Kotlin 代码,还会包括 Kotlin 附带的标准库,都会转换为 JavaScript。既可以转成前端使用的 JavaScript 代码,也可以生成后端使用的 JavaScript 代码。

  • Kotlin 实现前端的功能:
    1、 Kotlin 提供了大量的 API 操作 DOM 对象, 允许通过 DOM API 来动态创建和更新页面元素;
    2、 Kotlin 提供了支持 WebGL 的 API ,因此可以再网页上用 WebGL 创建图形元素。
    3、 Kotlin 也可以使用现有的前端库和框架 , 如 jQuery 和 RectJS 等
  • Kotlin 编译成后端 JavaScript 代码时完全可以与后端 JavaScript (如 Node.js)进行交互。

1.3 开发 Android 应用

Google 官方推荐使用 Kotlin 作为 Android 的开发语言,说明 Kotlin 非常适合开发 Android 应用,Kotlin 完全兼容 JDK 1.6, 因此保证了基于 Kotlin 开发的 Android 应用完全可以在较老的 Android 设备上运行。
对于大部分 Android 应用开发者来说,大部分时候都是与 Android 应用程序框架层交互的,调用 Android 应用程序框架层的 API ,而 Kotlin 可以自由调用 Java 的各种类库,因此使用 Kotlin 调用 Android 应用程序框架层来开发应用程序甚至无需额外的学习,可以无缝过渡为使用 Kotlin 开发。
对于性能方面来说,Kotlin 编译的字节码与 Java 原生字节码极为相似。随着 Kotlin 对内联函数的支持,使用 Lambda 表达式的代码通常比用 Java 写的代码运行的更快,也正是因为这两点主要的优势,Kotlin 被 Google 当做 Android 开发的官方推荐语言。

2. 第一个 Kotlin 程序

这里我是在 Android studio 里直接操作的,Android studio 从 3.0 开始 内置了 Kotlin 运行时环境,我的是 4.0 的版本,创建一个Kotlin的文件以 扩展名 .kt 结尾类型的文件 “helloWorld.kt”如下:

package com.tho.mas

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

运行该文件会发现控制台输出如下结果:
在这里插入图片描述

分析:

Kotlin 程序与Java 程序不同,Kotlin 支持函数式编程,因此 Kotlin 程序只需要一个 main() 函数作为程序入口,不需要将该 main() 函数放在任何的类中。我们看一下上面的 main() 函数不难发现 它其实就是 Java 主类中的 main() 方法的变体。只是在 Kotlin 中,函数也是一等公民,因此函数可以独立存在,而 Java 主类中的 main() 方法则必须要放在类中声明。 Java 中的 main 方法 需要声明一个 String[] args 形参, 而 Kotlin 的 main() 函数也声明了一个 args: Array<String> 形参,它们都是一个字符串数组类型的形参,这表明它们的本质是一样的,只是表现形式不同。Kotlin 中的 fun 关键字适用于声明函数的。

3. Kotlin 语言的特点

和 Java 一样 Kotlin 也是一门强类型的语言,强类型包含两方面的含义:

  • 所有的变量必须先声明,后使用;
  • 指定类型的变量只能接收类型与之匹配的值
    强类型的语言可以再编译过程中发现源代码的错误,从而保证程序更加健壮。

Kotlin 语言里的分号 “;” 与 Java 不同, Kotlin 不强制要求每条语句必须以分号结尾,当然也可以使用分号表示语句结束。
Kotlin 对大小写敏感

3.1 Kotlin 语言中的注释

  • 单行注释 - - “//” 双斜杠 同 Java 中的单行注释一样;
  • 多行注释 - - “/* */” 和 Java 中的多行注释类似,区别在于,在 Kotlin 中 多行注释可以嵌套,但是觉得用到嵌套的注意意义不大;
  • 文档注释 - - “/** */” 和 Java 中的一样 ,需要使用 Dokka 工具生成 API 文档,且,源程序必须要有包名,否则 Dokka 工具是不会提取文档注释来生成 API 文档的。

3.2 声明变量

Kotlin 是强类型的语言,所以变量必须先声明后使用,且要指定变量的类型,使用 var 或者 val 关键字进行声明,语法格式如下:

var | val 变量名 [:类型] [= 初始值]

其中 var 声明的变量是可变的, val 声明的变量是不可以变的。

4. Kotlin 的数据类型-数值型

4.1 整型

与 Java 类似, Kotlin 提供了 4 中整型:

  • Byte: Byte 在内存中通常占 8 位,取值范围是 -128~127 。 兼容 Java 的 byte 和 Byte 类型;
  • Short: 在内存中占 16 位,取值范围 -32768 ~ 32767 。 兼容 Java 的 short 和 Short 类型;
  • Int: 占 32 位, 表示范围 -2147483648~2147483647 。 兼容 Java 的 int 和 Integer 类型;
  • Long: 占 64 位,表示范围是 -2 的 63次幂 ~ 2 的 63 次幂 -1 。兼容 Java 的 long 和 Long 类型;

如果声明一个常量或者变量没有指定数据类型 ,只是简单地指定了初始值为整数值,那么 Kotlin 会默认设置为 Int 类型。
Kotlin 的整型和 Java 中的整型是不一样的,Kotlin 中的整型不是基本数据类型,而是引用类型(大致相当于 Java 的包装类),Byte、Short、Int、Long 都继承自 Number 类型,一次等他们都可调用方法、访问属性。可以通过访问不同整数类型的 MIN_VALUE 和 MAX_VALUE 来获取对应类型的最小值和最大值:

println(Short.MIN_VALUE)// 输出 Short 的最小值
println(Short.MAX_VALUE) // 输出 Short 的最大值

注意:Kotlin 是 null 安全的语言,因此 Byte、Short 、Int、Long 型变量都不能接受 null 值,如果要存储 null 值,则应该使用 Byte?、 Short?、Int?、Long? 类型

//Int 类型不支持 null 值, 所以下面的代码是错误的
var num: Int = null
// Int? 类型相当于支持 null 值的 Int 类型,所以下面的代码是对的
var num2: Int? = null

也就是说在类型后面加上?其实就是让该类型支持接受 null 值。此外,整数类型添加 ? 后缀还有一个区别:普通类型的整型变量将会映射成 Java 的基本类型;带 ? 的整数类型将会映射成基本类型的包装类。
例如:

var comNum1:Int = 200 //使用 Int 声明的变量对应 Java 中的基础数据类型 int
var comNum2:Int = 200
println(comNum1 === comNum2) // 基本类型的比较,基本类型在栈内存中是同一个对象,所以输出 true
var objNum1:Int? = 200 // 使用 Int? 声明的变量对应 Java 中的 Integer 类型
var objNum2:Int? = 200
println(objNum1 === objNum2) // 引用类型比较,堆内存中,值相等但是地址不同,输出 false 

但是将 objNum1 设置为 -128~127 之间的整数做“===”比较会输出 true , 这是由 Java 的 Integer 类的特征决定的(-128 ~127 之间的同一个整数自动装箱成 Integer 实例时,永远都是引用 cache 数组的同一个数组元素,所以它们完全相等,在此范围外的整数自动装箱成 Integer 实例时, 系统总是重新创建一个 Integer 实例)

Kotlin 的证书数值 3 种表示方式:

  • 十进制: 普通的整数数值表示;
  • 二进制: 以 0b 或 0B 开头后面是0和1的组合的整数数值都是二进制 如 0B1010110;
  • 十六进制: 以 0x 或者 0X 开头的证书数值 10~15 分别以 a ~ f (不区分大小写)来表示,如: 0XaF
  • Kotlin 不支持八进制

为了提高数值(包括浮点型)的可读性,Kotlin 允许为数值增加下划线作为分隔符,比如:

val num = 123_342_567 //这里的 num 的实际值 为 123342567

4.2 浮点型

  • Float: 32 位的浮点型,当精度要求不高时可以使用此类型;
  • Double: 64 位的双精度浮点型,当程序要求存储很大或者高精度的浮点数时使用该类型;

Kotlin 中的浮点数有两种表示形式:

  • 十进制数表示形式: 例如 5.12、 13.14、0.12等。浮点数必须包含一个小数点,否则会被当成整数类型处理;
  • 科学计数表示形式: 例如 5.12e2(即 5.12 x 10的2次方,e也可以大写);

需要注意的是:只有浮点型的数值才可以使用科学计数形式表示,比如 52100 是一个 Int 型的值, 但是 521E2 是浮点类型的值 。

4.3 字符型

Char 类型 ,Kotlin 中的 Char 类型不能当整数值使用,即 Char 型变量或表达式不能赋值给整型变量,整形变量或表达式也不能赋值给 Char 型变量。

4.4 数值型之间的类型转换

Kotlin 与 Java 不同,Kotlin 不支持取值范围小的数据类型隐式转换为取值范围大的类型,由于不同整型支持的表数范围存在差异,因此进行类型转换时必须注意选择合适的类型。Kotlin 为所有数值类型都提供了如下方法进行转换:

toByte()  //转换为 Byte 类型
toShort() // 转换为 Short
toInt() //转换为 Int
toLong()	// 转换为 Long
toFloat()	//转换为 Float
toDouble()	//转换为 Double
toChar() 	//转换为 Char

Kotlin 要求不同整型的变量或值之间必须进行显示转换,比如:

var num: Byte = 20
//var num1: Short = num //kotlin 不允许隐式转换数据类型,所以这行代码不能通过编译,是错误的
var num1: Short = num.toShort()//这样才对

5. Kotlin 的数据类型- 非数值型

5.1 Boolean 类型

逻辑 上的“真” 或 “假” ,与 Java 类似, Kotlin 的 Boolean 类型的值只能是 true 或 false, 不能用 0 或者非 0 来代表,其他数据类型的值不能转换成 Boolean 类型。 Boolean 类型映射为 Java 中的 boolean 基本类型,Boolean?映射为 Java 中的 包装类 Boolean。

5.2 null 安全

null 安全可以说是 Kotlin 对 Java 的重大改进之一,这样可以避免 Java 编程时令人恐惧的 NullPointerException (简称 NPE)。但 null 安全只不过是各种现代编程语言玩剩下来的东西。

非空类型和可空类型
只有可空类型的变量或常量才能接受 null ,非空类型的变量或常量不能接受 null。Kotlin 对可空类型进行了限制,如果不加任何处理,可空类型不允许直接调用方法、访问属性。因此,通过可空类型与非空类型的区分,Kotlin 即可在程序中避免空指针异常。比如:

var aStr: String = "Hello"
var bStr: String? = "Hello"
aStr = null // aStr 不能接受 null 所以错误
bStr = null // bStr 能接受 null 正确

println(aStr.length)// 因为 aStr 不可能为 null ,所以运行时不可能导致 NPE
println(bStr.length) // 因为 bStr 是可空类型,被Kotlin限制,不能直接调用方法、访问属性,所以编译就不通过,运行不了所以也不可能 NPE 

可空类型的变量不允许直接调用方法或属性,但可以先判读该变量不为 null ,然后再调用该变量的方法或属性。例如:

var str: String? = "Hello" //定义一个可空变量 str
//需要先判断 str 不为null 才能调用其属性或者方法 
var len = if (str != null) str.length else -1
println("str的长度为:${len}")

对于非空的 Boolean 可以接受 3 个值: true、false 、null。因此 对于 Boolean? 类型的变量进行判断时,要显示与true、false值进行比较判断。

var boo: Boolean? = null
if (boo == true) {
	//逻辑代码
}

如果将上述的代码改成下面的方式则会报错无法编译:

if(boo){

}

报错“type mismatch: inferred type is Boolean? but Boolean was expected” ,因为 Kotlin 的 if 条件必须是 Boolean 类型 ,而 Boolean? 类型 和 Boolean 类型其实是两种不同的类型

安全调用
先看一个例子:

var str: String? = "hello"
println(str?.length)//输出5
str = null
println(str?.length)//输出 null

这种可空变量后面再加上?号的方式去调用方法或者属性的方式为安全调用

Elvis 运算 ?:
先来看一段代码:

var str: String? = "hello"
var len = if(str!=null) str.length else -1
str = null
var len1 = str?.length ?: -1 //Elvis 运算

“?:” 左边的表达式不为null 返回左边表达式的值,否则返回 它右边的值,这个跟 Java 中的三目运算有点像,只是Kotlin 中没有三目运算, “?:” 其实就是 if 分支的简写。

强制调用
强制调用是为 NPE 爱好者准备的,不管变量是否为 null ,程序都直接调用该变量的方法或属性,用“!!”即可,但是这样强制调用可能引发 NPE

var str:String? = "hello"
println(str!!.length) // 输出5
str = null
println(str!!.length)// NPE

5.3 字符串

5.3.1 字符串类型

Kotlin 的字符串有两种字面值:

  • 转义字符串:可以有转义字符,很像 Java 中的字符串;
  • 原始字符串:包含换行和任意文本,需要使用 3 个引号引起来
var str = "Hello" //转义字符串
println(str.length)
var txt = """
		|鹅鹅鹅,
		|曲项向天歌。
		""".trimMargin()//原始字符串,
println(txt)

编程时往往会在原始字符串中进行一些缩进,但这些缩进并不是原始字符串希望包含的,使用 trimMargin() 方法可以去掉原始字符串前面的缩进,在默认情况下 Kotlin 使用 “|” 作为边界符。也就是说 “|”竖线前的内容都会被去掉。也可以自定义边界符,只需要在 trimMargin(“此处传入边界符”) 方法中传入参数值即可。

5.3.2 字符串模板

Kotlin 允许在字符串中嵌入变量或表达式,只要将变量或表达式放入 ${} 即可。Kotlin 就会吧变量或表达式的值替换该区域,字符串模板不仅可以再普通字符串中使用,也可以在原始字符串中使用:

var num = 88
var str = "Hello ${num}" //普通字符串中嵌入变量
val rand = java.util.Random() // 创建 Java 的 Random 对象
var str1 = "随机数:${rand.nextInt(10)}"//字符串模板中嵌入方法调用
println("${str1}的长度是${str1.length}")

5.3.3 Kotlin 字符串特有的方法

  • toXxx() 将字符串转换成数值 ,比如 str.toDouble()
  • capitalize() 首字母大写
  • decapitalize() 首字母小写
  • contains() 支持正则表达式匹配,而 Java 中不支持

类型别名
Kotlin 提供了类似 C 语言中的 typedef 的功能,可以为已有的类型指定另一个可读性更强的名字,Kotlin 提供了 typealias 来定义类型别名,语法格式如下

typealias 类型别名 = 已有类型

例如:

typealias NodeSet = Set<Network.Node> // 给 Set<Network.Node> 指定了更短的别名

var set: NodeSet //可以直接使用类型别名去命名变量

大多情况下我们可以通过定义别名为内部类起一个更短的名字,比如:

class A {
	inner class Inner
}
class B {
	inner class Inner
}
typealias AInner = A.Inner	// 给 A 的内部类定义一个别名
typealias BInner = B.Inner

var aa: AInner = A().AInner()
var b = B().BInner()

其实 Kotlin 本身也大量利用了别名这个功能,比如利用别名建立了 Kotlin 类 和 Java 类之间的关系,下面是 Kotlin 集合体系中定义的别名:

typealias kotlin.ArrayList<E> = java.util.ArrayList<E>
typealias kotlin.HashMap<K,V> = java.util.HashMap<K,V>
typealias kotlin.HashSet<E> = java.util.HashSet<E>
typealias kotlin.LinkedHashMap<K,V> = java.util.LinkedHashMap<K,V>
typealias kotlin.LinkedHashSet<E> = java.util.linkedHashSet<E>
typealias kotlin.RandomAccess = java.util.RandomAccess

Kotlin 的 Lambda 表达式是一个差别较大的特色功能: Java 的 Lambda 表达式的类型时函数式接口,而 Kotlin 的 Lambda 表达式的类型直接就是函数类型,因此 Kotlin 也允许为 Lambda 表达式的类型指定别名。例如:

typealias Predicate<T> = (T) -> Boolean // 为 (T) -> Boolean 类型指定别名 Predicate<T>
val p: Predicate<String> = { it.length > 4} //使用 Predicate<String> 定义变量,该变量的值是一个 Lambda 表达式
println(arrayOf("Kotlin","Java","C""Python").filter(p))// 为filter 方法传入 p 参数,只保留长度大于 4 的字符串
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值