Kotlin 自学笔记
项目未来可能需要使用
kotlin
开发,所以特此记录一下学习笔记,仅供参考,方便后期查询。已同步到GitHub
上:KotlinTest
Kotlin 简介
kotlin
的目标是成为一门全栈语言,主要有以下的特点:
- 已经成为
Android
的官方推荐语言 - 百分百的和
java
兼容,两者可以相互转换 JS
、JVM
、Native
多平台开发
数据类型
1. 基本类型
Boolean true/false
Double 64
Float 32
Long 64
Int 32
Short 32
Byte 8val aChar = '0'
val bChar = '我'
val cChar = '\u000f'
复制代码
Char
类型的转义字符
\t 制表符
\b 光标后退一个字符
\n 回车
\r 光标回到行首
\' 单引号
\" 双引号
\\ 反斜杠
\$ 美元符号,Kotlin 支持美元符号开头的字符串模板
复制代码
2. 基本类型的转换
不可隐式转换
val anInt: Int = 5
val aLong: Long = anInt.toLong()
复制代码
必须得通过.to类型
的方式进行数据的转换
字符串
一串
Char
用双引号""引起来
val aString: String = "Hello World!"
字符串比较
a == b 表示比较内容 类似 Java 中的 equals a === b 表示比较对象是否相同
字符串模板
println("hello, $name") -> "hello, 小明"
3. Koltin 中的类和对象初始化
类的定义
- 类,一个抽象的概念
- 具有某些特征的事物的概括
- 不特定指代任何一个具体的事物
一般写法:
/**
* 其中类参数如果加上 var 修饰,那么他便是成员变量,反之则是普通的参数
*/
class Student(var name: String, var age: Int){init {// ... 相当于构造函数中的代码}
}
复制代码
对象
是一个具体的概念,与类相对
描述某一个类的具体个体
举例:
某些人、领导的车等等
类和对象的关系
- 一个类通常可以有很多歌具体的对象
- 一个对象本质上只能从属一个类
- 某一个人,他是工程师,但本质上还是属于人这一类
一般写法:
val student: Student = Student("xiaweizi", 23)
复制代码
类的继承
- 提取多个类的共性得到一个更为抽象的类,即父类
- 子类拥有父类的一切特征
- 子类也可以定义自己的特征
- 所有的类最终继承自
Any
,类似于java
中的Object
4. 空类型和智能转换
空类型
// 定义
val notNull: String = null // 错误,不可能为空
val nullanle: String? = null // 正确,可以为空
// 使用
notNull.length // 正确,不可能为空所以可以直接使用
nullable.length // 有可能为空,不能直接获取长度
// 要想获取长度,可以通过以下两者方式
nullable!!.length // 正确,强制认定 nullable 不可能为空,如果为空则会抛出空指针异常
nullable?.length // 正确,若 nullable 为空,则返回 null
复制代码
智能类型转换
val child: Child = parent as Child // 类似于 Java 的类型转换,失败则抛出异常
val child: Child = parent as? Child // 如果转换失败,返回 null
复制代码
编译器智能识别转换:
val parent: Parent = Child()
if (parent is Child) {// parent 直接调用子类方法,不需要再进行强制转换
}val string: String = null
if (string != null) {// string.length 可以直接调用length 方法
}
复制代码
5. 区间
一个数学上的概念,表示范围, ClosedRange
的子类,IntRange
最常用
基本用法:
0..100 --> [0, 100]
0 until 100 --> [0, 100)
i in 0..100 表示 i 是否在区间[0, 100]中
复制代码
6. 数组
基本写法:
val ints: IntArray = IntArrayOf(1,2,3,5)
var charArray: CharArray = charArrayOf('a', 'b', 'c', 'd', 'e')
var stringArray: Array<String> = arrayOf("aa", "bb", "cc", "dd", "e")
复制代码
基本操作:
print(charArray[index])
ints[0] = 2
ints.length
cahrArray.joinToString("") // 讲 char 数组转换成字符串
stringArray.slice(1..4) // 取出区间里的值
复制代码
程序结构
1. 常亮和变量
常量
val a = 2
类似 Java 中的 final
不可被重复赋值
运行时常量:val x = getX()
编译期常量:const val x = 2
复制代码
变量
var a = 2
a = 3 // 可以被再次赋值
复制代码
类型推导
val string = "Hello" // 推导出 String 类型
val int = 5 // 推导出 Int 类型
var x = getString() + 5 // String 类型
复制代码
2. 函数 Function
以特定功能组织起来的代码块
// 最简单的打印信息,无返回的方法
fun printMessage(message: String):Unit{println("$message")
}
// 拥有返回值得方法
fun sum(first: Int, second: Int):Int {return first + second
}
// 可以简化成:
fun sum(first: Int, second: Int) = first + second
// 或者更简单的匿名函数
val result = fun(first: Int, second: Int) = first + second
复制代码
3. Lambda 表达式
其实又是匿名函数
一般形式:
{传入参数 -> 函数体,最后一行是返回值}
// 例如
val sum = {first: Int, second: Int -> first + second}
val printMessage = {message: String -> println(message)}
复制代码
类型标识
() -> Unit // 无参,返回值为 null
(Int) -> Int // 传入整型,返回一个整型
(String, (String) -> String) -> Boolean // 传入字符串、Lambda 表达式,返回Boolean
复制代码
Lambda 表达式的简化
- 函数参数调用时最后一个
Lambda
可以移出去 - 函数参数只有一个
Lambda
,调用时小括号可以省略 Lambda
只有一个参数可默认为it
- 入参、返回值与形参一致的函数可以用函数引用方式作为实参传入
4. 成员变量和成员方法
成员变量的声明
// 第一种是在构造函数中声明
class Student(var age: Int, name: String){// age 是成员变量 name 是局部变量
}
// 第二种是在函数体内声明
var a = 0get() {field += 1return field}set(value) {println("set)field = value + 1}
// 可以进行对 get 和 set 方法的重新定义// 属性的初始化尽量在构造方法中完成
// var 用 lateinit 延迟初始化, val 用 lazylateinit var sex: String
val person: Person by lazy {Person()
}
复制代码
成员方法
在类中直接声明方法可以直接调用,包括lambda
表达式
// 方法的声明
fun sum(a: Int, b: Int) = a + b
val sum1 = {a: Int, b: Int -> a + b}
// 方法的调用
println(person.sum(1,2))
println(person.sum1(3,5))
复制代码
5. 运算符
在java
中运算符是不能重新定义重载的,只能按照原先的逻辑进行计算
而Kotlin
则可以重新定义运算符,使用operator
关键字,举了例子:
// 定义一个用于计算复数的类
class Complex(var real: Double, var imaginary: Double) {operator fun plus(other: Complex): Complex{return Complex(real+other.real, imaginary+other.imaginary)}// 重新 toString 方法overrride fun toString(): String {return "$real + ${imaginary}i"}
}
// 使用
val complex1 = Complex(1, 2)
val complex2 = Complex(2, 3)
println(complex1 + complex2)
// 输出结果为
"3 + 5i"
复制代码
关键就是这个方法,方法名必须是plus
或者其他官方定义的运算符,参数有且仅有一个,类型自定义,返回值意识可以自定义的.
operator fun plus(other: Complex): Complex{return Complex(real+other.real, imaginary+other.imaginary)
}
复制代码
6. 表达式
中缀表达式
通过infix
关键字修复方法,那么就可以不用通过 对象.方法() 的方式调用,而是直接 对象 方法名 参数的方式调用。举了例子
class Student(var age: Int){infix fun big(student: Student): Boolean {return age > student.age}
}
// 如果没有 infix 的调用方式:
println(Student(23).big(Student)(12))
// 如果使用 infix 修饰的调用方式:
println(Student(23) big Student(12))
复制代码
if
表达式
直接来个例子
val a = 20
val b = 30
val flag: Int = if(a > b) a else b
复制代码
When
表达式
加强版的 switch
,支持任意类型, 支持纯粹表达式条件分支(类似if
),举个栗子:
val a = 5
when(a) {is Int -> println("$a is Int")in 1..6 -> println("$a is in 1..6")!in 1..4 -> println("$a is not in 1..4")else -> {println("null")}
}
复制代码
for
循环
基本写法
for (element in elements)
复制代码
while
循环
基本写法
while() {
}
do {
} while()
复制代码
跳过和终止循环
跳过当前循环用 continue
终止循环用 break
复制代码
6. 异常捕获
同样也是表达式,可以用来赋值,举个例子
return try{x/y}catch(e: Exception) {0} finally {//...}
复制代码
如果没有异常则返回x/y
,否则返回0
,finally
中的代码无论如何还是要执行的。
7. 具名参数、变长参数和默认参数
**具名参数:**给函数的实参附上形参
fun sum(first: Int, second: Int) = first + second
sum(second = 2, first = 1)
复制代码
**变长参数:**用varary
修饰,使用起来是和数组一样,某个参数可以接收多个值,可以不作为最后一个参数,如果传参时有歧义,需要使用具名参数。
fun hello(vararg ints: Int, string: String) = ints.forEach(println(it))
hello(1,3,4,5,string = "hello")
// 如果最后一个参数也是 Int
fun hello(varary ints: Int, anInt: Int)
// 创建数组
val arrayInt: IntArray = intArrayOf(1, 2, 3, 4)
hello(ints = *arrayInt, anInt = 2)
复制代码
**默认参数:**就是给参数传入一个默认的值
fun hello(anInt: Int = 1, string: String)
hello(string = "aaa")
复制代码
面向对象
1. 继承
继承语法要点:
- 父类需要
open
才可以被继承 - 父类方法、属性需要
open
才可以被覆写 - 接口、接口方法、抽象类默认为
open
- 覆写父类(接口)成员需要
override
关键字
语法要点:
class A: B(), C, D
- 继承类时实际上调用了父类的构造方法
- 类只能单继承,接口可以多实现
接口代理:
一个类可以直接将自己的任务委托给接口的方法实现,举个例子:
interface Drive{fun drive()
}interface Sing{fun sing()
}class CarDrive: Drive{override fun drive() {println("我会开车呦")}
}class LoveSing: Sing{override fun sing() {println("我会唱歌呦")}
}class Manager(drive: Drive, sing: Sing): Drive by drive, Sing by singfun main(args: Array<String>) {val carDrive = CarDrive()val loveSing = LoveSing()val manager = Manager(carDrive, loveSing)manager.drive()manager.sing()
}
复制代码
这样,manager
不用做任何事情,完全交付给接口实现.
接口方法冲突:
接口方法可以有默认实现,通过super<父类名>
.方法名
interface A{fun a() = 0
}interface B{fun a() = 1
}interface C{fun a() = 2
}class D(var aInt: Int): A,B,C{override fun a(): Int {return when(aInt){in 1..10 ->{super<A>.a()}in 11..100 ->{super<B>.a()}else -> {println("dd")super<C>.a()}}}
}
复制代码
2. 类及成员的可见性
跟java
类似,private、protected、public
,其中internal
代表的是模块内可见
3. Object
相当于Java
中的单例模式,有以下特点
只有一个实例的类
不能自定义构造方法
可以实现接口、继承父类
本质上就是单例模式最基本的实现
interface getDataSuccess{fun success()}abstract class getDataField{abstract fun failed()}object NetUtil: getDataField(), getDataSuccess{override fun success() {println("success")}override fun failed() {println("failed")}val state: Int = 0fun getData(): String = "请求成功"} 复制代码
3. 伴生对象和静态成员
相当于java
中的静态方法
每个类可以对应一个伴生对象
伴生对象的成员全局独一份
如果
java
中想直接调用kotlin
中的静态方法或者静态变量,可以考虑使用JvmField JvmStatic
.open class Util private constructor(var anInt: Int) {companion object {@JvmStaticfun plus(first: Int, second: Int) = first + secondfun copy(util: Util) = Util(util.anInt)@JvmFieldval tag = "tag"}} 复制代码
4. 方法的重载
通过给方法的参数配置默认值,即可实现方法的重载,按理说,一切可以拥有默认值的方法重载才是合理的方法重载。
名称形同、参数不同,跟返回值没有关系
class OverLoadTest {@JvmOverLoadsfun a(anInt: Int = 0, string: String="") = 1
}val test = OverLoadTest()
test.a(1, "")
test.a()
test.a(anInt = 2)
test.a(string = "")
复制代码
使用JvmOverLoads
是为了方便Java
中调用方法的重载.
5. 扩展方法
kotlin
中的扩展方法,我认为相当于java
中的代理模式,拿到被代理的对象,然后进行一系列的操作。
fun String.add(anInt: Int): String {var sb = StringBuilder()for (i in 0 until anInt) {sb.append(this)}return sb.toString()
}operator fun String.times(anInt: Int): String {var sb = StringBuilder()for (i in 0 until anInt) {sb.append(this)}return sb.toString()
}// 使用
var string = "xiaweizi"
println(string.add(5))
println(string * (3))
复制代码
6. 属性代理
类似之前说的var anInt: Int by lazy{2}
,懒赋值就是使用的属性代理,来看个例子:
fun main(args: Array<String>) {val a: Int by DelegatesTest()println(a)var b: Int by DelegatesTest()b = 3println(b)
}class DelegatesTest {private var anInt: Int? = nulloperator fun getValue(thisRef: Any?, property: KProperty<*>): Int {println("getValue")return anInt?:0}operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int): Unit {println("setValue")this.anInt = value}
}
复制代码
val
对应getValue
,var
对应getValue和setValue
方法,这个时候声明的属性就全权交付给DelegatesTest
类中的anInt
代理,当anInt
为空的时候返回0
,否则返回anInt
.
7. JavaBean
使用data
修饰类,类似java
中的javaBean
,默认实现了set get toString
等方法,并拥有componentN
方法.
不过有个缺点就是,无法被继承,没有无参构造函数,可以通过安装allOpen
和noArg
插件解决这个问题.
data class UserBean(var name: String, var age: Int)val userBean: UserBean = UserBean("小芳", 23)
println(userBean.name)
println(userBean.toString())println(userBean.component1())
println(userBean.component2())val (name, age) = userBean
println("name: $name")
println("age: $age")
复制代码
至于这种写法
val (name, age) = userBean
,是因为定义了component1
的运算符
class Complex{operator fun component1() = "你好呀"operator fun component2() = 2operator fun component3() = 'a'
}val complex = Complex()
val (a, b, c) = complex
println(a + b + c)
复制代码
使用起来也是很简单的
8. 内部类
- 定义在类内部的类
- 与类成员有相似的访问控制
- 默认是静态内部类,非静态用
inner
关键字 this@Outter
this@Inner
的用法- 匿名内部类
- 没有定义名字的内部类
- 类名编译时生成,类似
Outter$1.class
- 可继承父类,实现多个接口,与
Java
注意区别
举个例子:
class Outer{var string: String = "outer"class Inner1{var string: String = "inner1"fun sum(first: Int, second: Int) = first + second}inner class Inner2{var string: String = "inner2"fun cha(first: Int, second: Int) = first - secondfun getInnerField() = this.stringfun getOuterField() = this@Outer.string}
}fun main(args: Array<String>) {val inner1 = Outer.Inner1()val inner2 = Outer().Inner2()println(inner1.sum(1, 2))println(inner2.cha(2, 1))println(inner2.getInnerField())println(inner2.getOuterField())
}
复制代码
匿名内部类:
val listener: onClickListener = object : Father(), Mother, onClickListener{override fun sing() {println("mother sing")}override fun teach() {println("father teach")}override fun onClick() {println("匿名内部类")}
}
复制代码
使用
Object
实现匿名内部类
9. 枚举和密封类
枚举是对象可数,每个状态相当于每个对象,是可以传构造参数的
密封类时子类可数,在kotlin
大于1.1子类只需要与密封类在同一个文件加,保护子类的位置
sealed class SealedClassTest{class sum(first: Int, seocnd: Int): SealedClassTest()class cha(first: Int, seocnd: Int): SealedClassTest()object Bean: SealedClassTest()
}enum class HttpStatus(val anInt: Int){SUCCESS(0), FAILED(1), LOADING(2)
}fun main(args: Array<String>) {val class1 = SealedClassTest.cha(1, 2)println(HttpStatus.SUCCESS)
}
复制代码
高阶函数
1. 基本概念
- 传入或者返回函数的函数
- 函数引用
::println
- 带有
Receiver
的引用pdfPrinter::println
有三种显示
// 1. 包级函数
intArray.forEach(::print)// 2. 类.方法
intArray.forEach(Int::addOne)
fun Int.addOne(): Unit {println("addOne:$this")
}// 3. 对象.方法
intArray.forEach(AddTwo()::addTwo)
class AddTwo {fun addTwo(anInt: Int): Unit {println("addTwo:$anInt")}
}
复制代码
2. 常用的高阶函数
常用的高阶函数还是有很多的,会简单的使用例子即可:
// 遍历
fun forEachTest() {val strings: Array<String> = arrayOf("aa", "ee", "bb", "ll")strings.forEach { println(it) } // 遍历每一个值strings.forEachIndexed { index, s -> println("index:$index,String:$s") } // 遍历 下标和值一一对应}// 重新拷贝一个值
fun mapTest() {val strings: Array<String> = arrayOf("aa", "ee", "bb", "ll")var map = strings.map { "$it-test" }map.forEach { print("$it\t") }
}// 将集合合体
fun flatMapTest() {val lists = listOf(1..10,2..11,3..12)var flatMap = lists.flatMap {it.map {"No.$it"}}flatMap.forEach(::println)
}fun reduceTest() {val ints = listOf(2, 3, 4, 5)println(ints.reduce { acc, i ->acc + i})
}// 字符串连接
fun foldTest(){val ints = listOf(2, 3, 4, 5)println(ints.fold(StringBuffer(), { acc, i -> acc.append("$i,") }))println(ints.joinToString(","))
}fun filterTest() {val ints = listOf(1, 2, 3, 4, 5, 6)println(ints.filter { element -> element % 2 == 0 })
}// 当值不是奇数就去,遇到偶数就停止了
fun takeWhileTest() {val ints = listOf(1, 3, 3, 4, 5, 6)println(ints.takeWhile { it % 2 != 0 })
}fun letTest() {findPerson()?.let { (name, age) -> println("name:$name, age:$age") }findPerson()?.apply { println("name:$name, age:$age") }with(findPerson()!!) { println("name:$name, age:$age") }
}data class Person(val name: String, val age: Int)fun findPerson(): Person? {return Person("aa", 23)
}
复制代码
3. 复合函数
有点类似数据中的f(g(x))
fun main(args: Array<String>) {val add1 = {int: Int ->println("add1")int + 1}val add2 = {int : Int ->println("add2")int + 2}var add3 = add1 addThen (add2)println(add3(4))
}infix fun <P1, P2, R> Function1<P1, P2>.addThen(function: Function1<P2, R>): Function1<P1, R> {return fun(p: P1): R{return function.invoke(this.invoke(p))}
}
复制代码
4. Currying
简单来说就是多元函数变换成一元函数调用链式,举个简单的例子,这是优化之前:
fun log(tag: String, out: OutputStream, message: String){out.write("[$tag], $message".toByteArray())
}
复制代码
优化之后
fun log(tag: String)= fun(out: OutputStream)= fun(message: String)= out.write("[$tag], $message".toByteArray())
复制代码
5. 计算文件字符串个数的小例子
首先将字符串转换成字符串数组:
val map: HashMap<Char, Int> = HashMap()
var toCharArray = File("build.gradle").readText().toCharArray()
复制代码
通过分组的方式,统计每个字符串的个数,并打印:
toCharArray.groupBy { it }.map { it.key to it.value.size }.forEach { println(it) }
复制代码
kotlin
和java
的混合开发
1. 基本的交互操作
属性读写
Kotlin
自动识别Java Getter/Setter
Java
操作Kotlin
属性通过Getter/Setter
空安全类型
Kotlin
空安全类型的原理- 平台类型
Platform Type
Java
可以通过@Nullable、@NotNull
几类函数的调用
- 包级函数:静态方法
- 扩展方法:带
Receiver
的静态方法 - 运算符重载:带
Receiver
的对应名称的静态方法
几个常用的注解
@JvmField
:将属性编译为Java变量
@JvmStatic
:将对象的方法编译成功Java
静态方法@JvmOverloads
:默认参数生成重载方法@JvmName
:制定Kotlin
文件编译后的类名
NoArg 和 AllOpen
NoArg
为被标注的类生成无参构造AllOpen
为被标注的类去掉final
,允许被继承
正则表达式
- 用
Raw
字符串定义正则表达式 Java
的Pattern
Kotlin
的Regex
举个例子:
val source = "Hello This my phone number: 010-12345678."
val pattern = """.*(\d{3}-\d{8}).*"""Regex(pattern).findAll(source).toList().flatMap(MatchResult::groupValues).forEach(::print)复制代码
Kotlin 自学笔记相关推荐
- 字节跳动大佬的Python自学笔记.pdf
1. 字节跳动大佬的Python自学笔记 这是我的一个朋友自学资料包,通过这个资料包自学拿到了字节跳动的Offer, 下面是他之前入门学习Python时候的学习资料,非常全面,从Python基础.到w ...
- Kotlin 学习笔记(八)—— Kotlin类与对象之接口
Kotlin 学习笔记(八)-- Kotlin类与对象之接口 Kotlin学习笔记系列教程 Kotlin 学习笔记(一)-- 概述.学习曲线.开发工具.参考资料 Kotlin 学习笔记(二)-- 基础 ...
- JAVA自学笔记07
JAVA自学笔记07 1.构造方法 1) 例如:Student s = new Student();//构造方法 System.out.println(s);// Student@e5bbd6 2)功 ...
- MySQL自学笔记2--select的5个子句
MySQL自学笔记 使用的MySQL自带命令客户端,其中具体的操作是在自建的数据库下room303表中进行的,表中的列有:id.name.age.email.tel.salary.riqi.class ...
- JAVA自学笔记24
JAVA自学笔记24 1.能使用同步代码块就使用同步代码块,除非锁对象是this,就可以考虑使用同步方法.静态方法的锁是类的字节码对象. 2.JDK5新特性 1)接口Lock void Lock()/ ...
- 怎么用vc采集ni卡数据_SystemLink自学笔记(6):SystemLink架构和数据服务
1. SystemLink架构和数据服务 1.1. 架构和特点 现在在对SystemLink的功能有了一个大概的了解后,可以进一步从它的整体架构学习这门新技术了.NI官网给出了白皮书,原文是英文资料, ...
- JAVA自学笔记22
JAVA自学笔记22 1.操作基本数据类型的流 DataInputStream DataOutputStream 数据输出流允许应用程序以适当方式将基本的Java数据类型写入输出流中.然后,应用程序可 ...
- Kotlin学习笔记(3)- 语法
系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正.如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步!kotlin学习笔记系列首发简书和CSDN Kotlin学习笔记(1)- 环 ...
- Kotlin学习笔记(2)- 空安全
系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正.如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步!kotlin学习笔记系列首发简书和CSDN Kotlin学习笔记(1)- 环 ...
最新文章
- (C++)从字符串中取出整形、浮点型和字符串
- centos7.3网络实验环境(金个人)
- Rolling element bearing diagnostics using the Case Western Reserve University data-学习笔记
- Web框架——Flask系列之设置和读取cookie(十五)
- 动环监控系统接线图_机房动环监控系统报价
- 如何实现HashMap顺序存储
- ASP脚本语言之vbscript用法
- win2008r2 or centos6 硬盘挂载
- ES19-Java API全文搜索
- 办公室海王小姐姐悄悄问我如何在PC端登录多个微信小号?
- m_Orchestrate learning system---三十一、模板和需求的关系
- opecv-putText
- SpringBoot整合shiro的一个例子
- linux向苹果手机传文件,堪比 AirDrop,iPhone 与 Windows 电脑互传文件的三种方式
- 大使馆大师傅活动分工和非都市规划
- 100G QSFP28 AOC有源光缆的分类及应用
- nginx 区分手机浏览器和pc浏览器
- vue -【nextTick】-【过度与动画】-【插槽】-【配置代理服务器】-【vuex】-【路由】
- Java实现咖啡馆选餐系统
- JavaApi:IO基础