基础知识Kotlin开发Android开发

Kotlin学习笔记

2017-09-04  本文已影响754人  下位子

项目未来可能需要使用kotlin开发,所以特此记录一下学习笔记,仅供参考,方便后期查询。已同步到GitHub上:KotlinTest

Kotlin 简介

kotlin 的目标是成为一门全栈语言,主要有以下的特点:

数据类型

1. 基本类型

Boolean true/false
Double 64
Float  32
Long   64
Int    32
Short  32
Byte   8

val aChar = '0'
val bChar = '我'
val cChar = '\u000f'

Char类型的转义字符

\t          制表符
\b          光标后退一个字符
\n          回车
\r          光标回到行首
\'          单引号
\"          双引号
\\          反斜杠
\$          美元符号,Kotlin 支持美元符号开头的字符串模板

2. 基本类型的转换

不可隐式转换

val anInt: Int = 5
val aLong: Long = anInt.toLong()

必须得通过.to类型的方式进行数据的转换

字符串

3. Koltin 中的类和对象初始化

类的定义

一般写法:

/**
* 其中类参数如果加上 var 修饰,那么他便是成员变量,反之则是普通的参数
*/
class Student(var name: String, var age: Int){
    init {
        // ... 相当于构造函数中的代码
    }
}

对象

类和对象的关系

一般写法:

val student: Student = Student("xiaweizi", 23)

类的继承

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 表达式的简化

4. 成员变量和成员方法

成员变量的声明

// 第一种是在构造函数中声明
class Student(var age: Int, name: String){
    // age 是成员变量 name 是局部变量
}
// 第二种是在函数体内声明
var a = 0
    get() {
        field += 1
        return field
    }
    set(value) {
        println("set)
        field = value + 1
    }
// 可以进行对 get 和 set 方法的重新定义

// 属性的初始化尽量在构造方法中完成
// var 用 lateinit 延迟初始化, val 用 lazy

lateinit 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. 继承

继承语法要点:

语法要点:

接口代理:

一个类可以直接将自己的任务委托给接口的方法实现,举个例子:

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 sing

fun 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中的单例模式,有以下特点

3. 伴生对象和静态成员

相当于java中的静态方法

4. 方法的重载

通过给方法的参数配置默认值,即可实现方法的重载,按理说,一切可以拥有默认值的方法重载才是合理的方法重载。

名称形同、参数不同,跟返回值没有关系

class OverLoadTest {
    @JvmOverLoads
    fun 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 = 3
    println(b)
}

class DelegatesTest {
    private var anInt: Int? = null
    operator 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 对应 getValuevar对应getValue和setValue方法,这个时候声明的属性就全权交付给DelegatesTest类中的anInt代理,当anInt为空的时候返回0,否则返回anInt.

7. JavaBean

使用data修饰类,类似java中的javaBean,默认实现了set get toString等方法,并拥有componentN方法.

不过有个缺点就是,无法被继承,没有无参构造函数,可以通过安装allOpennoArg插件解决这个问题.

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() = 2
    operator fun component3() = 'a'
}

val complex = Complex()
val (a, b, c) = complex
println(a + b + c)

使用起来也是很简单的

8. 内部类

举个例子:

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 - second
        fun getInnerField() = this.string
        fun 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. 基本概念

有三种显示

// 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) }

kotlinjava的混合开发

1. 基本的交互操作

属性读写

空安全类型

几类函数的调用

几个常用的注解

NoArg 和 AllOpen

正则表达式

举个例子:

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)
上一篇 下一篇

猜你喜欢

热点阅读