Kotlin第二章:kotlin基础

1. 基础数据类型

Kotlin第二章:kotlin基础_第1张图片

1. 整数类型

序号 类型 位宽 最小值 最大值
1 Byte 8 -128 127
2 Short 16 -32768 32767
3 Int 32 -2,147,483,648 (-2^31) 2,147,483,647 (2^31 - 1)
4 Long 64 -9,223,372,036,854,775,808 (-2^63) 9,223,372,036,854,775,807 (2^63 - 1)
val number =  100 //默认Int类型 类比java的Integer
val bigNumber = 80000000000 //超过Int类型默认Long
val longNUmber = 20L //手动添加L 标明Long类型
val byteNumber: Byte = 1 //晟敏层Byte类型
  • 创建一个kt文件
// 类似于Java中的public static void main(String[] args){}
// kotlin中存在两种main方法的写法,都可以
// var相当于是一个变量 val对比java中相当于是final修饰的变量,不允许修改
fun main(){
    var intNum = 100
}
fun main(args: Array<String>){
    val num = 100
}

2. 浮点类型

序号 类型 位宽 小数精度
1 Float 32 6位
2 Double 64 15-16位

Kotlin 对于小数的默认推断是Double类型。如果需要显式将一个小数指定为Float类型需要在数值尾部加入fF。由于Float类型十进制位数是6位,所以例子中floatNumber实际值大小为3.1415926,后面就会出现进度丢失舍弃。

在 Kotlin 中还有一点与 Java 不同的是,Kotlin 中数字不存在隐式的拓宽转换。比如一个函数参数为Double 的函数只能接收 Double 类型,不能接收 FloatInt 或者其他数字类型

val doubleNumber = 3.1415928888  //默认是Double类型
val floatNumber  = 3.1415928888f //尾部加f或F显式表示这是一个Float类型的浮点数

3. 字符类型

在Kotlin中,字符类型用Char表示

fun testChar(char: Char){
    if(char == '4'){
        //TODO
    }
}

4. 布尔类型

在 Kotlin 使用Boolean表示布尔类型,它只有两个值 truefalse。注意可空类型Boolean?类型会存在装箱操作。

var isVisible: Boolean = false
val isVisible = false    //自动推断为布尔Boolean类型

5. 字符串类型

在 Kotlin 中字符串用 String 类型表示。字符串是不可变的。 字符串的元素——字符可以使用索引运算符访问: s[i]。 可以用 for 循环迭代字符串:

val str="1234567890"

for(char in str) {
    println(char)
}

6. 字符串模板

使用""" 创建模板字符串

  1. 创建模板字符串
var stringTemplate = """
	创建模板字符串
"""
  1. 去除前序空格
stringTemplate = stringTemplate.trimMargin()
  1. 使用 $ 替换模板中的内容
fun main(){
	showAge(17)
}

fun showAge(age: Int){
    // 如果传过来是一个对象的话需要加 {} 在里边调用属性
    println("输入的年龄是 ${age}")
    println("输入的年龄是 $age")
    // 可以用来代替java中的写法
    println("输入的年龄是" + age)
}

7. 类型转换

通过toByte等函数转换

序号 类型 强转函数
1 Byte toByte()
2 Short toShort()
3 Int toInt()
4 Long toLong()
5 Float toFloat()
6 Double toDouble()
7 Char toChar()
val number = 100
number.toString()
number.toByte()
...

8. 位运算

Kotlin 中的位运算和 Java 不同的是没有用特殊符号来表示,可以采用了中缀函数方式调用具名函数。

  • shl(bits) – 有符号左移【shl是Shift Logical Left的缩写】
  • shr(bits) – 有符号右移
  • ushr(bits) – 无符号右移
  • and(bits) – 位
  • or(bits) – 位
  • inv() – 位
  • xor(bits) – 位异或
val vip= true
val admin= false

val result = vip and(admin) =false 

val result = 8 ushr(2) = 2

2. 数据容器

容器是用于存放数据的载体。容器分为数组、集合。

Kotlin作为一门全新的语言,肯定还是要有自己的容器类,不然哪天Java跟Kotlin划清界限,那麻烦就大了。

Kotlin也拥有三类基本的容器,分别是集合Set、队列List、映射Map,每种容器又分作只读与可变两种类型这是为了判断该容器能否进行增删改等变更操作。

1. 数组

数组是一种初始化时指定容器大小,不可以动态调整其大小的容器。元素按顺序存储在一串连续的内存段上

1. 创建数组

1. arrayOf创建数组

创建一份数组并赋值元素,数组内可以是任意元素

val array = arrayOf(1, 2, 3)                     
val array = arrayOf(1, true, "2", JSONObject())  //

2. arraysOfNulls创建数组

创建一个指定大小的、所有元素都为空的数组,但必须指定集合中的元素类型

var arrayOfNulls = arrayOfNulls<String>(10)

3. 使用 … 创建闭区间数组

创建出来一个闭合区间 这个与下边的4准确的说应该是是属于Range

var numArr = 1 .. 9 //打印出来 1-9
var charArr = 'a' .. 'z' // 打印出来 a - z 

4. 使用 until 创建开区间数组

创建出来一个左闭右开的数组 [n,m),

var intRange = 1 until 9  //数组中只有1-8

5. 使用构造函数初始化数组

使用Array的构造函数,动态创建数组

// 这个函数会创建五个元素,分别是 0 1 4 9 16
var intArr = Array(5){i -> (i * i).toString()}

2. 原生类型数组

Kotlin中提供了不需要拆装箱的原生类型数组,

Kotlin数组类型不是集合的一种,但是**数组和集合可以互相转换,初始化集合的时候也可以传入一个数组**

序号 原生类型数组 描述
1 ByteArray 字节型数组
2 ShortArray 短整型数组
3 IntArray 整型数组
4 LongArray 长整型数组
5 BooleanArray 布尔数组
6 CharArray 字符型数组
7 FloatArray 浮点型数组
8 DoubleArray 双精度浮点型数组

3. 数组常见操作

1. 获取元素

val array = arrayOf(1,2,3,4,5,6,7,8,9,0)
array[4] //获取下标是4的元素
array.component1() // 获取数组中第一个元素 注意是第一个,不是下标 1
...
array.component5() // 获取数组中第五个元素

2. for-in 循环

//用法与js中的 for in 类似,比较与java的增强for少了一个元素类型
for(item in array){
    println(item)
}

3. for循环-索引遍历

for(index in array.indices){
    println("下标 $index 的元素是 ${array[index]}")
}

4. for循环-带索引遍历

与上一节相比这个可以同时使用index和元素

for((index,item) in array.withIndex()){
    println("下标 $index 的元素是 $item")
}

5. foreach遍历

forEach遍历,循环中的元素固定是it

array.forEach{
    println(it) //默认就是it ,不需要声明这个变量
}

6. forEach增强

同时遍历下标和元素

array.forEachIndexed { index,item ->
    println("$index ,$item")
}

7. 数组翻转

array.reverse()

2. 集合

Kotlin 标准库提供了一整套用于管理集合的工具,集合是可变数量(可能为零)的一组条目,各种集合对于解决问题都具有重要意义,并且经常用到。与数组不同的是可变集合的大小可以动态改变。

  • List: 是一个有序集合,可通过索引(反映元素位置的整数)访问元素。元素可以在 list 中出现多次。列表的一个示例是一句话:有一组字、这些字的顺序很重要并且字可以重复。
  • Set: 是唯一元素的集合。它反映了集合(set)的数学抽象:一组无重复的对象。一般来说 set 中元素的顺序并不重要。例如,字母表是字母的集合(set)。
  • Map: (或者字典)是一组键值对。键是唯一的,每个键都刚好映射到一个值,值可以重复。
  • 虽说List和Set是不可变集合,但是也可以通过list = list.plus(element)的方式添加元素后重新给集合赋值
数组创建方式 示例 说明 是否可变
arrayListOf() mutableListOf 相同元素类型的队列 val array = arrayListOf(1, 2, 3) val array = mutableListOf() 必须指定元素类型 可变
listOf() 相同元素类型的集合 val array = listOf(1, 2, 3) -必须指定元素类型 - 必须指定初始化数据元素 不可变
arrayMapOf() mutableMapOf 相同元素类型的字典 val array= arrayMapOf(Pair(“key”,“value”)) val array= mutableMapOf() 初始元素使用Pair包装 可变
mapOf() 相同元素类型的字典 val array= mapOf(Pair(“key”,“value”))
var map = mapOf(“key” to “value”)
元素使用Pair包装 必须指定初始元素 不可变
arraySetOf() mutableSetOf 相同元素类型的集合 val array= arraySetOf(1,2,3) val array= mutableSetOf() - 会对元素自动去重 可变
setOf() 相同元素类型的集合 val array= arraySetOf(1,2,3) - 对元素自动去重 - 必须指定元素类型。 不可变

1. Set

与java一样Set是无序排列的

1.1 Set

只读集合,一般来说初始化之后不可更改,但是也可以通过plus或者是plusElement更改元素

var set = setOf<String>("123","456")
set = set.plus("0")
println(set) //打印123 456 0 

1.2 MutableSet

创建的可变集合,但是对集合内元素操作也有限制,与Java中的Set操作方法类似

  1. add方法只能添加元素,但是因为Set是无序的,所以不知道元素添加的位置

  2. 没有修改元素值的方法,应该可以通过iterator进行remove和add操作来实现

  3. remove方法只能删除指定的元素,不能通过下标删除

  4. 对于循环操作,提供了for-in 、迭代器、forEach等

  5. for-in

var phoneSet = mutableSetOf<String>("小米", "红米", "oppo")
// 类似js中的forin或者是java中的增强for
for(phone in phoneSet){
    println(phone)
}
  1. 迭代器
//并不能像 java那样 ,下班这个在kotlin中是不可用的
// for(Iterator iterator = phoneSet.iterator();iterator.hasNext();){}
var iterator = phoneSet.iterator()
while(iterator.hasNext()){
    println(iterator.next())
}
  1. forEach

在foreach中固定使用it代替循环中的每一个元素,不能替换成其他的变量

// 在forEach中使用 it代替,固定的it不能修改成其他的变量
phoneSet.forEach{ println("$it")}

2. List

也是分为List和MutableList,List不可修改(也可以通过 list = list.plus(“”)修改),MutableList可以

2.1 List

与Set类似,没有add方法,但是可以通过plus plusElement添加元素

var list = listOf<String>("123")
list = list.plus("456")
list = list.plusElement("789")
println(list)

2.2 MutableList

可变的集合,支持增删改查操作,常用的方法有

  1. get 与Java类似,可以获取指定位置的元素
  2. add 在集合末尾添加一个元素,也可以使用add(index,element)在指定位置添加元素,其后的元素下标+1
  3. set 替换或修改指定位置的元素
  4. removeAt 删除指定位置上的元素
  5. 循环,除了和set类似的三种遍历方式,还多了一种带下标的遍历方式
fun main() {
    val strList = listOf("买鸡蛋","买肉","买菜")
    for ((index,element) in strList.withIndex()) {
        println("$index $element")
    }
}
  1. sort 排序相关的方法
var listOf = mutableListOf<String>("123", "4564", "78944")
// 这种写法和循环一样,每一个元素都是it且是正序排列的
listOf.sortBy { it.length }
println(listOf)
// 这个方法是按照指定规则倒序排列的
listOf.sortByDescending{it.length}
println(listOf)

3. Map

Map可以按照java中的put方法赋值,但是Kotlin建议用map[“K”] = val

fun main() {
    val map = TreeMap<String,String>()
    map["A"] = "1"
    map["B"] = "2"
    map["C"] = "3"
    map.put("D","4")
    println(map)
}

Map初始化的方法有所不同,一共有两种方式

  • A to B
  • Pair(A,B)
var mapOf = mapOf("test" to "test", Pair("test1", "test1"))
println(mapOf)

3. 方法参数和Lambda表达式

1. 方法

  1. 方法由关键字fun 声明
  2. 后边紧跟着方法名称
  3. 括号里是方法的参数,名称在前 :类型在后
  4. 最后返回写返回类型
fun addNum(firstNum: Int,lastNum: Int) :Int{
    return firstNum + lastNum
}

1. 普通类中的方法

class Person{
    fun eat(){
        println("人需要吃饭")
    }
}
fun main(){
    Person().eat()
}

2. companion object实现static功能

使用companion object实现类似java中的static功能

class Person{
    // 不需要创建爱你对象,可以直接使用类名.方法调用
    companion object{
        fun eat(){
            println("吃饭")
        }
    }
}
fun main(){
    Person.eat()
}

3. 静态类的方法

使用**关键字 object**声明静态类

object Person(){
    fun eat(){
        println("人需要吃饭")
    }
}
fun main(){
    Person.eat()
}

4. 简化方法

  1. 当方法体中只有一行的时候,可以直接写在方法体后边
// 当然这里的返回值类型也可以不用写,可以由编译器自动推断类型
fun addNum(a: Int,b: Int) :Int = a + b
  1. 直接创建出来函数
fun main(){
	val sum = {a: Int,b: Int -> a + b}    
    println(sum(4, 16))
}
  1. 直接创建出来函数,但是参数放后边
fun main(){
    val sum = (Int,Int) -> Int = {x,y -> x + y}
    println(sum(4,6))
}

2. 参数

1. 默认参数

方法参数可以有默认值,当**省略相应的参数是会使用默认值,与java相比可以减少重载方法的数量**

val PI = 3.14f 
fun main(){
    /**
    * 因为这里circumference方法中的Pi参数的值已经知道了是固定的PI
    * 所以需要只需要指定radius的值就可以了
    * 这里边的Pi就是默认参数,每次都是固定值
    * radius 就是具名参数
    */
    println(circumference(radius = 2f))
}
fun circumference(Pi:Float = PI,radius:Float) = 2 * Pi * radius

2. 具名参数

在上一章节中,如果第一个参数使用默认值的话 radius就是具名参数

如果默认参数之后的最后一个参数是lambda表达式,那么他既可以作为具名参数在括号内传入,也可以在括号外传入

// 当方法不需要返回值的时候需要使用 Unit
fun foo(bar: Int = 0,baz: Int = 1, action: () -> String){
    val actionString = action()
}
//括号内接收返回值
foo(1,2,action ={
    val result = 2
    // 在方法体中的最后一句就是返回值,所以不需要写return 
    "inside String"
})
//括号外传递值,当且仅当最后一个参数是lambda函数的时候才能写在括号外边
foo(){
    "括号外传递action参数"
}

3. 可变参数

相当于是Java中的 … ,在Kotlin中使用**vararg 关键字**

fun append(vararg str: Char) :String{
    val result = StringBuffer()
    for(char in str){
        result.append(char)
    }
    return result.toString()
}

fun main(){
    val appendStr = append('S','t','r','i','n','g')
    println(appendStr)
}

可变参的要求

  • 只有一个参数可以标注为 vararg;
  • 如果 vararg 参数不是列表中的最后一个参数, 可以使用具名参数语法传递其后的参数的值,或者,如果参数具有方法类型,则通过在括号外部传一个 lambda。

当我们调用 vararg 方法时,我们可以一个接一个地传参,例如 append(‘h’, ‘e’, ‘l’, ‘l’, ‘o’),或者,如果我们已经有一个数组并希望将其内容传给该方法,我们使用伸展**(spread)操作符(在数组前面加 *)**:

val world = charArrayOf('w','o','r','l','d')
val result = append('h','e','l','l','o',' ',*world)

3. 局部方法

kotlin 支持局部方法,即一个方法在另外一个方法内部,局部方法可以方位外部方法中的变量

fun magic(): Int {
    fun foo(v: Int): Int {
        return v * v
    }

    val v1 = (0..100).random()
    return foo(v1)
}

4. Lambda表达式

Lambda表达式的本质其实是匿名方法,因为在其底层实现中还是通过匿名方法来实现的。但是我们在用的时候不必关心起底层实现。不过Lambda的出现确实是减少了代码量的编写,同时也是代码变得更加简洁明了

  • 寻常方法
view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(v.getContext(), "Lambda简洁之道", Toast.LENGTH_LONG).show();
    }
});
  • Labmda表达式

匿名方法,可传递

view.setOnClickListener { v -> Toast.makeText(v.context, "Lambda简洁之道", Toast.LENGTH_LONG).show() }

1. Lambda语法

无参数的情况

var arg = {//TODO}

有参数的情况

val arg : (参数类型,参数类型) -> 返回值类型 = {参数1,参数2 -> 操作代码}
// 等价于下边这种写法:即表达式的返回值类型会根据操作的代码自推导出来。
var 变量名 = { 参数1 : 类型,参数2 : 类型, ... -> 操作参数的代码 }

2. 保留关键字it

  • it并不是Kotlin中的一个关键字(保留字)
  • it是在当一个高阶方法中Lambda表达式的参数只有一个的时候可以使用it来使用此参数
  • it可表示为单个参数的隐式名称,是Kotlin语言约定的
var arrayOf = arrayOf("1", 1, 2, false)
arrayOf.forEach { println(it) }

3. 下划线 _

在lambda表达式中不需要使用这个参数的时候,可以使用 _代替

fun main() {
    var mapOf = mapOf<String, Any>("1" to "2", "4" to false)
    mapOf.forEach{ (key, value) ->
        println("$key : $value")
    }
    mapOf.forEach{(_,value) ->
        println(value)
    }
}

4. 条件控制

Kotlin条件控制
if-else表达式
when表达式
when表达式增强版
when代替多层嵌套if-else
像java中一样if -else else-if
代替switch case
kotlin1.3之后对when表达式内部允许传值操作
when也存在返回值,对于多级嵌套if来说更方便

1. if表达式

kotlin中的if表达式会返回一个值,如果不写return语句的话,会默认返回if表达式作用域的最后一行

fun maxNum(a: Int,b: Int){
    if(a > b){
        return a
    }else{
        return b
    }
}

1. 代替三目运算符

因为kotlin中的if-else自带return返回值,所以没有像java中的三目运算符,可以使用下面的例子代替

fun minNum(a :Int,b :Int){
    return if(a < b) a else b
}

2. when表达式

使用when代替了switch-case语句,如果when表达式需要有返回值的话,则必须要带有else分支

fun judgeScore(score:Int){
    when(score){
        10 -> println("满分牛逼")
        9 -> println("也不错")
        8 -> println("还可以")
        else -> println("继续努力")
    }
}

3. when增强

在Kotlin 1.3版本以后,可以通过其他方法获取when判断的值

fun main(args: Array<String>) {
    when (val value = getValue()) {//when表达式条件直接是一个表达式,并用value保存了返回值, 实际上相当于把外部那一行缩进来写
        is Int -> "This is Int Type, value is $value".apply(::println)
        is String -> "This is String Type, value is $value".apply(::println)
        is Double -> "This is Double Type, value is $value".apply(::println)
        is Float -> "This is Float Type, value is $value".apply(::println)
        else -> "unknown type".apply(::println)
    }
}

fun getValue(): Any {
    return 100F
}

4. 使用when代替if else

if 表达式一样,when表达式也是带有返回值的。建议对于多层条件级或嵌套条件控制的使用建议使用when替代if-else

fun eval(number: Number) {
    if (number is Int) {
        println("this is int number")
    } else if (number is Double) {
        println("this is double number")
    } else if (number is Float) {
        println("this is float number")
    } else if (number is Long) {
        println("this is long number")
    } else if (number is Byte) {
        println("this is byte number")
    } else if (number is Short) {
        println("this is Short number")
    } else {
        throw IllegalArgumentException("invalid argument")
    }
}


//多层级条件使用when表达式
fun eval(number: Number): String = when (number) {
    is Int -> "this is int number"
    is Double -> "this is double number"
    is Float -> "ths is float number"
    is Long -> "this is long number"
    is Byte -> "this is byte number"
    is Short -> "this is Short number"
    else -> "invalid number"
}

5. 循环控制

1. for循环

*for 循环* 可以对任何提供迭代器(iterator)的对象进行遍历,for 循环仅以唯一一种形式存在, 和 Java的 for-each 循环一致。其写法for in 和 C# 一样。和 Java 类似,循环最常见的应用就是迭代集合,具体语法如下:

  1. for-in循环

类似java中的增强for但是不需要写明类型

var list = listOf<Int>(1,2,3,4,5)
for(item in list){
    println("$item")
}
  1. 遍历索引
for(index in list.indices){
    println("$index")
}
  1. 带索引遍历
for((index,item) in listOf.withIndex()){
    println("下标是 $index 的元素是$item")
}

2. while 和do … while

与java中的一直,不做过多赘述

3. 迭代区间和数列

  1. 创建闭合区间
var arr = 1 .. 10
// 输出 1 2 3 4 5 6 7 8 9 10
arr.forEach { println(it)} 
  1. 创建左闭右开区间
var arr = 1 until 5
// 输出 1 2 3 4 
arr.forEach { println(it)}
  1. 创建递减闭合区间
var arr = 5 downto 1
// 输出 5 4 3 2 1
arr.forEach { println(it)}
  1. 设置for循环的步长
var arr = 1 .. 10 
// 输出 1 3 5 7 9
for(it in arr step 2){
    println(it)
}

6. 泛型

泛型种类 Java 中代码示例 Kotlin 中代码示例 说明
泛型类型 class Box class Box 泛型类型
泛型方法 T fromJson(String json, Class tClass) fun fromJson(json: String, tClass: Class): T? 泛型函数
有界类型参数 class Box class Box 泛型类型约束
上界通配符 void sumOfList(List list) fun sumOfList(list: List) 泛型上限约束
下界通配符 void addNumbers(List list) fun addNumbers(list: List) 泛型下限型约束

1. 泛型接口/类

定义反应类型,是在类型名之后,主构造函数之前用尖括号起的大写字母类型参数指定:

  • 泛型接口
interface Drinks<T>{
    fun price(t: T)
    
    fun taste(): T
}
  • 泛型类
abstract class Color<T>(var t: T/*泛型字段*/) {
    abstract fun printColor()
}

class Blue {
    val color = "blue"
}

class BlueColor(t: Blue) : Color<Blue>(t) {
    override fun printColor() {
        println("color:${t.color}")
    }

}

2. out 与in

kotlin中可以通过out与in的关键字,指定泛型的上限和下限

1. out

标注允许传入 T 及 T的子类

// 系统的类ArrayList声明了一个泛型T
class ArrayList<T>{

}
// Type mismatch.Required:  ArrayList , but Found:ArrayList
// 虽然Int是Number的子类,但kotlin认为ArrayList,不是 ArrayList的子类,所以会编译报错
val arrayList:ArrayList<Number> = ArrayList<Int>()//编译报错
// 在定义处使用out关键字声明,允许传入的泛型参数可以是T以及T的子类
class ArrayList<out T>{

}
// 也就是传入的泛型参数可以是 Number及Number的子类Int,Double,Float....
val arrayList:ArrayList<Number> = ArrayList<Int>()//编译正常

// 使用处使用out关键字声明
val arrayList:ArrayList<out Number> = ArrayList<Int>()//编译正常

2. in

标注可以传入 T或者T的父类

// 在定义处使用out关键字声明,允许传入的泛型参数可以是T以及T的子类
class ArrayList<T>{

}

val arrayList:ArrayList<Int> = ArrayList<Number>()//编译报错
class ArrayList<in T>{

}
// 也就是传入的泛型参数可以是 Number及Number的子类Int,Double,Float....
// 使用处使用out关键字声明
val arrayList:ArrayList<Int> = ArrayList<Number>()//编译正常
val arrayList:ArrayList<in Int> = ArrayList<Number>()//编译正常,此时in可写可不写

3. 泛型方法

fun <T> fromJson(json: String, tClass: Class<T>): T? {
    val t: T? = tClass.newInstance()
    return t
}

fun main(){
    var str = fromJson<String>("{}",String::class.java)
}

4. 泛型约束

// 泛型类型限定-1
// 所传递的类型T必须满足是User的子类 或User类
// 类似于 Java中的 ? extends User
fun <T: User> fromJson(json: String, tClass: Class<T>): T? {
    ...
}
// 泛型类型限定-2
// 所传递的类型T必须同时满足 where 子句的所有条件,在下面的示例中,类型 T 必须既实现了 User, 也实现了 Comparable。
fun <T> fromJson(json: String, tClass: Class<T>): T? where T : User,T : Comparable<T> {
    ...
}

7. 面向对象

1 创建类

1.1 类对象

与Java创建的类有较大不同

class Ractangle(var height:Int,var width:Int)

fun main() {
    var rectangle = Rectangle(5, 10)
    println("矩形的高度是${rectangle.height}")
    println("矩形的宽度是${rectangle.width}")
}

1.2 方法

与Java不同的是 main方法好像不能写到类里边

class Girl(var name:String,var chactor:String,var voice:String){
    fun smile(){
        println("妹子笑声很${voice}")
    }

    fun status(){
        println("妹子状态比较${chactor}")
    }
}

fun main() {
    var girl = Girl("测试", "彪悍", "甜美")
    girl.smile()
    girl.status()
}

1.3 内部变量

与 java 类型,但是需要给出默认值

package net.lesscoding.oop

class WashMachine(var name:String,var size:Int) {
    var isOpenDoor = true
    var currentModel = 0

    fun openDoor(){
        this.isOpenDoor = true
        println("打开洗衣机的门")
    }

    fun closeDoor(){
        this.isOpenDoor = false
        println("打开洗衣机的门")
    }

    fun startWash(){
        if(!isOpenDoor){
            when(currentModel){
                1 -> println("开始轻柔的洗衣服")
                2 -> println("开始狂暴的洗衣服")
                else -> println("模式错误")
            }
        }else {
            println("门没关")
        }
    }
    fun chooseModel(model:Int){
        this.currentModel = model
        when(model){
            1 -> println("轻柔模式")
            2 -> println("狂暴模式")
            else -> println("模式错误")
        }
    }
}

fun main() {
    var washMachine = WashMachine("海尔", 5)
    washMachine.openDoor()
    washMachine.closeDoor()
    washMachine.chooseModel(2)
    washMachine.startWash()
}

1.4 封装

相当于java中的private

private fun speed(speed:Int){
    println("转速$speed")
}

1.5 继承

相较于Java 感觉异常麻烦,如果你的类或者是方法想要被子类继承的话,需要添加open关键字

  • Fathor.kt
open class Fathor {
    var chactor:String = "性格内向"
    open fun action(){
        println("公共场合喜欢大声呼喊")
    }
}
  • Son.kt

使用 :Fathor() 代表继承父类 ,重写方法由@Override变更为了override关键字

class Son :Fathor(){
    override fun action(){
        println("喜欢安静")
    }
}

2多态 和抽象类

2.1 抽象类

和Java一样,但是如果父类有属性的话,需要写很多数据

  • Human抽象父类
abstract class Human (var name:String){
    abstract fun eat()
}
  • Man类

这里继承父类也需要写上父类的属性,比较麻烦

class Man(name: String) :Human(name){
    override fun eat() {
        println("$name 喜欢大口大口的吃")
    }
}
  • Woman类
class Woman(name:String) :Human(name) {
    override fun eat() {
        println("$name 喜欢小口慢嚼")
    }
}
  • 测试类
fun main() {
    var person1 = Man("普信男")
    person1.eat()
    var person2 = Woman("普信女")
    person2.eat()
    // 输出
    //普信男 喜欢大口大口的吃
	// 普信女 喜欢小口慢嚼
}

3 接口

好像跟java也差不多

  • IMan接口
package net.lesscoding.oop.test03

interface IMan {
    fun eat()
}
  • 实现类
package net.lesscoding.oop.test03

class Man:IMan {
    override fun eat() {
        println("大口大口的吃")
    }
}
fun main(){
    var man = Man()
    man.eat()
}

4 委托和代理

相当于是一个中间商啊

  • 接口
package net.lesscoding.oop.test04

interface IWashBow {
    fun washing()
}
  • 代理类
package net.lesscoding.oop.test04

class BigHeadSon :IWashBow{
    override fun washing() {
        println("大头儿子洗一次碗赚了1块")
    }
}
  • 委托类

在这个类中首先实现了接口IWashBow 然后使用 by关键字委托BigHeadSon()来实现washing() 方法

package net.lesscoding.oop.test04

class SmallHeadFather :IWashBow by BigHeadSon(){
}

当然这个类依旧可以实现接口的方法

package net.lesscoding.oop.test04

class SmallHeadFather :IWashBow by BigHeadSon(){
    override fun washing() {
        println("我要外包给大头儿子洗")
        BigHeadSon().washing()
        println("血赚9块钱")
    }
}
  • 存在的问题

SmallHeadFather 虽然让BigHeadSon代理了接口实现,但是在类中调用了两次BigHeadSon(),这就会创建两个BigHeadSon对象,所以需要把BigHeadSon变成单例的.

只需要把class关键字换成object即可。

委托类需要修改一下生成

package net.lesscoding.oop.test04

object BigHeadSon :IWashBow{
    override fun washing() {
        println("大头儿子洗一次碗赚了1块")
    }
}
package net.lesscoding.oop.test04

class SmallHeadFather :IWashBow by BigHeadSon{
    override fun washing() {
        println("我要外包给大头儿子洗")
        BigHeadSon.washing()
        println("血赚9块钱")
    }
}

此时同时创建两个代理类比较内存地址为一样的

var bigHeadSon = BigHeadSon
var bigHeadSon2 = BigHeadSon
println(bigHeadSon == bigHeadSon2) //true

5 枚举

较为麻烦,还需要多写一个class, 且想要什么参数需要加在类的参数上

package net.lesscoding.oop.test05
// 注意这里如果不写var的话 下边第二个info就获取不出来
enum class Week(var index:Int,var info:String) {
    Monday(1,"星期一"),
    Tuesday(2,"星期二"),
    Wednesday(3,"星期三"),
    Thursday(4,"星期四"),
    Friday(5,"星期五"),
    Saturday(6,"星期六"),
    Sunday(7,"星期日")
}

fun main() {
    println(Week.Friday)
    println(Week.Friday.info)
    println(Week.Sunday.ordinal)
}

6 印章类

相当于是一个类里边有**数量有限**的子类,需要使用关键字sealed
使用is 关键字代替 java中的instanceof

与 enum相比较 Sealed class更在意类型,枚举更在意数据

package net.lesscoding.oop.test05

sealed class Person {
    fun say(){
        println("Hello World")
    }

    class Man:Person()

    class Woman:Person()
}

fun main(){
    var man:Person = Person.Man()
    var woman:Person = Person.Woman()
    var man2:Person = Person.Man()

    var personList = listOf<Person>(man, woman, man2)
    for (person in personList) {
        if(person is Person.Man){
            person.say()
        }
    }
}

8. 集合处理

定义一个集合,包含20条数据

package net.lesscoding.stream

data class Girl(var name: String, var age: Int, var height: Int, var address: String)

var databaseTest = listOf<Girl>(
        Girl("依儿", 18, 168, "山东"),
        Girl("笑笑", 19, 175, "河南"),
        Girl("小百合", 17, 155, "福建"),
        Girl("michel", 22, 148, "广东"),
        Girl("猫咪", 28, 159, "广西"),
        Girl("玲儿", 23, 169, "广东"),
        Girl("环环", 25, 172, "安徽"),
        Girl("胖嘟嘟", 32, 180, "河北"),
        Girl("乔乔", 35, 180, "广东"),
        Girl("小可爱", 27, 150, "江西"),
        Girl("一生有你", 22, 163, "山东"),
        Girl("敏儿", 28, 155, "黑龙江"),
        Girl("月儿", 25, 178, "吉林"),
        Girl("花儿", 21, 183, "山东"),
        Girl("s小糖", 49, 190, "新疆"),
        Girl("悦悦", 19, 160, "广西"),
        Girl("小可爱", 29, 158, "广东"),
        Girl("紫琪", 49, 149, "新疆"),
        Girl("糖心", 26, 165, "甘肃"),
        Girl("棒棒糖", 23, 172, "浙江"),
        Girl("猪猪侠", 18, 173, "山东"),
        Girl("喵喵", 27, 164, "河南"),
        Girl("安琦", 19, 159, "河北"),
        Girl("叶子", 20, 160, "广东")
)

1 传统函数

传统函数应对不同的需求,一般会复制出来不同的方法,需求越来越多的时候,函数就会变得越来越多,如下

  1. 查询河南的女孩信息
fun findByAddress(address :String?){
    for(girl in databaseTest){
        if(girl.address.equals(address!!)){
            println("${girl.address} ${girl.name}")
        }
    }
}
  1. 查找小于24岁的女孩信息
fun findLessAge(age:Int){
    for(girl in databaseTest){
        if(girl.age < age){
            println("${girl.address} ${girl.name} ${girl.age}")
        }
    }
}
  1. 查找广东且小于28身高在165以上的学生信息

可以看到在kotlin中可以使用 and关键字代替 && 但是需要用括号将后边的包起来,同样的还有 or ( || ) xor (^) 等

fun findByAddressAgeHeight(address: String,age: Int,height: Int){
    for(girl in databaseTest){
        if(girl.age < age && girl.address.equals(address) and (girl.height > height)){
            println("${girl.address} ${girl.name} ${girl.age} ${girl.height}")
        }
    }
}

2 高阶函数

java中的stream流相差不大,但是里边的对象都必须用it表示

1. maxBy

按照某一个属性求最大值

// 按照年龄字段排序取最大值
var maxAgeGirl = databaseTest.maxBy { it.age }

2. minBy

按照某一属性求最小值

//按照height字段取最小值的对象
var minAgeGirl = databaseTest.minBy { it.height}

3. filter

过滤集合中的数据,返回一个boolean类型的值

// 过滤年龄小于20且身高在165以上的数据
var filterList = databaseTest.filter {
    it.age < 20 && it.height >= 165
}

4. map

把某个属性映射成新的集合

//把集合中的名称拿出来做一个集合
var nameList = databaseTest.map { it.name}

5. any

按照条件查询集合,如果有的话返回true 没有的话返回false

// 判断集合中有没有元素的age属性是18
var ageFlag = databaseTest.any { it.age == 18}

6. count

查找集合中符合条件的元素的个数

//查找age < 20的个数
var ageLess20 = databaseTest.count{ it.age < 20}

7. find

查找第一个符合条件的元素

var firstGuangDong  databaseTest.find{ it.address == "广东"}

8. findLast

查找最后一个符合条件的元素

var lastGuangDong = databaseTest.findLast {it.address == "广东"} 

9. groupBy

按照某个属性分组,返回一个Map,可以同个get方法获取里边的值

var groupByAddress = databaseTest.groupBy { it.address }

9. 扩展方法

1. DSL扩展函数

给集合添加拓展函数

fun  List<Girl>.findByAgeLess(age :Int) {
    filter { it.age < age }.forEach(::println)
}

databaseTest.findByAgeLess(20)

使用infix 关键字可以将方法调用中的 . 省略掉替换成空格

infix fun  List<Girl>.findByAgeLess(age :Int) {
    filter { it.age < age }.forEach(::println)
}

databaseTest findByAgeLess 20

2. 在Kotlin中定义

class Jump {
    fun test() {
        println("jump test")
        //在被扩展的类中使用
        doubleJump(1f)
    }
}

fun Jump.doubleJump(howLong: Float): Boolean {
    println("jump:$howLong")
    println("jump:$howLong")
    return true
}

Jump().doubleJump(2f)
//在被扩展类的外部使用
Jump().test()

3. 在java中调用kotlin扩展方法

Android Studio中点击 Tools,然后点击Kotlin --> Show Kotlin Bytecode,最后点击Decompile就可以查看kotlin编译之后的java文件

KotlinExtensionKt.doubleJump(new Jump(), 2.0f);

4. 泛型扩展方法

//泛型化扩展函数
fun <T> MutableList<T>.swap1(index1: Int, index2: Int) {
    val tmp = this[index1]
    this[index1] = this[index2]
    this[index2] = tmp
}

val test2 = mutableListOf("Android Q", "Android N", "Android M")
test2.swap1(0,1)
println(test2)

5. 扩展属性

扩展属性提供了一种方法能通过属性语法进行访问的API来扩展。尽管它们被叫做属性,但是它们不能拥有任何状态,它不能添加额外的字段到现有的Java对象实例。

//为String添加一个lastChar属性,用于获取字符串的最后一个字符
// Kotlin中任何属性都有get 和 set方法
val String.lastChar: Char get() = this.get(this.length - 1)

///为List添加一个last属性用于获取列表的最后一个元素,this可以省略
val <T>List<T>.last: T get() = get(size - 1)

val listString = listOf("Android Q", "Android N", "Android M")
println("listString.last${listString.last}")

6. 为伴生对象添加扩展函数

就像伴生对象的常规成员一样:可以只使用类名作为限定符来调用伴生对象的扩展成员:

class Jump {
    companion object {}
}
fun Jump.Companion.print(str: String) {
    println(str)
}

Jump.print("伴生对象的扩展")

10. Kotlin常用扩展

1. let

// kotlin中对let的定义
fun <T, R> T.let(f: (T) -> R): R = f(this)

let扩展函数的实际上是一个作用域函数,当你需要去定义一个变量在一个特定的作用域范围内,那么let函数是一个不错的选择;let函数另一个作用就是可以避免写一些判断null的操作。

fun main() {
    letTest("123")
    letTest(null)
}

fun letTest(str: String?){
    str.let {
        var str2 = "Android"
        println("$str2 it")
    }

    str?.let {
        println("$it 的长度是 ${it.length}")
    }
}

2. run

// kotlin中对let的定义
fun <T, R> T.run(f: T.() -> R): R = f()

run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式,在run函数中可以直接访问实例的公有属性和方法

data class Room(val address: String, val price: Float, val size: Float)

fun testRun(room: Room) {
    room.run {
        println("Room:$address,$price,$size")
    }
}

3. apply

调用apply方法之后, 就不需要通过对象.方法了 ,可以直接使用方法

调用某对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象。

从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身。

  • 用例1
class Person{
    fun eat(){
        println("吃饭")
    }
    
    fun drink(){
        println("喝水")
    }
}
fun main(){
    var person = Person()
    // 平常写法
    person.eat()
    person.drink()
    
    //使用apply
    person?.apply {
        eat()
        drink()
    }
}
  • 用例2
fun testApply(){
    ArrayList<String>().apply {
        add("111")
        add("222")
        add("2223")
        // apply内部有一个隐式的对象 this
        println("$this")
    }.let {
        println(it)
    }
}

4. 扩展使用案例

使用Kotlin扩展为控件绑定监听器减少模板代码

  • 定义扩展
// 为Activity定义find扩展方法,通过资源id获取控件
fun <T: View> Activity.find(@IdRes id: Int): T{
    return findViewById(id)
}
// 为Int添加onClick扩展方法,用于为资源id对应的空间添加onClick监听
fun Int.onClick(activity: Activity,click: () -> Unit){
    activity.find<View>(this).apply {
        setOnClickListener {
            click()
        }
    }
}
  • 使用扩展
class MainActivity: AppCompatActivity{
    override fun onCreate(saveInstanceState: Bundle?){
        super.onCreate(saveIntanceState)
        // 绑定对应的xml资源文件
        setContentView(R.layout.activity_main)
        // 查询id是test的文本框
        val textView = find<TextView>(R.id.test)
        R.id.test.onClick(this){
            textView.text = "Kotlin扩展应用"
        }
    }
}

11. 使用Kotlin完成一个一元四则预算

package com.example.myapplication.test

import kotlin.system.exitProcess

fun main(){
    while (true){
        println("====请输入算术表达式====")
        var calcStr = readLine()
        println(calcSplit(calcStr))
        println("是否继续,(y/n)")
        var choose = readLine()
        if(choose.equals("n",true)){
            println("系统退出")
            exitProcess(0)
        }
    }
}

fun calcSplit(calcStr: String?): String{
    calcStr?.let {
        var symbol :String = ""
        if(it.contains("+")){
            symbol = "+"
        }
        if(it.contains("-")){
            symbol = "-"
        }
        if(it.contains("*")){
            symbol = "*"
        }
        if(it.contains("/")){
            symbol = "/"
        }
        var split = it.split(symbol)
        return calc(split,symbol).toString()
    }
    return "算术表达式错误"
}

fun calc(split:List<String>,symbols: String): Double{
    var firstNumber = split[0].toDouble()
    var lastNumber = split[1].toDouble()
   return when(symbols){
        "+" -> firstNumber + lastNumber
        "-" -> firstNumber - lastNumber
        "/" -> firstNumber / lastNumber
        "*" -> firstNumber * lastNumber
        else -> 0.0
    }
}

vity.find(this).apply {
setOnClickListener {
click()
}
}
}


- 使用扩展

```kotlin
class MainActivity: AppCompatActivity{
    override fun onCreate(saveInstanceState: Bundle?){
        super.onCreate(saveIntanceState)
        // 绑定对应的xml资源文件
        setContentView(R.layout.activity_main)
        // 查询id是test的文本框
        val textView = find(R.id.test)
        R.id.test.onClick(this){
            textView.text = "Kotlin扩展应用"
        }
    }
}

11. 使用Kotlin完成一个一元四则预算

package com.example.myapplication.test

import kotlin.system.exitProcess

fun main(){
    while (true){
        println("====请输入算术表达式====")
        var calcStr = readLine()
        println(calcSplit(calcStr))
        println("是否继续,(y/n)")
        var choose = readLine()
        if(choose.equals("n",true)){
            println("系统退出")
            exitProcess(0)
        }
    }
}

fun calcSplit(calcStr: String?): String{
    calcStr?.let {
        var symbol :String = ""
        if(it.contains("+")){
            symbol = "+"
        }
        if(it.contains("-")){
            symbol = "-"
        }
        if(it.contains("*")){
            symbol = "*"
        }
        if(it.contains("/")){
            symbol = "/"
        }
        var split = it.split(symbol)
        return calc(split,symbol).toString()
    }
    return "算术表达式错误"
}

fun calc(split:List<String>,symbols: String): Double{
    var firstNumber = split[0].toDouble()
    var lastNumber = split[1].toDouble()
   return when(symbols){
        "+" -> firstNumber + lastNumber
        "-" -> firstNumber - lastNumber
        "/" -> firstNumber / lastNumber
        "*" -> firstNumber * lastNumber
        else -> 0.0
    }
}

你可能感兴趣的:(kotlin,Android,kotlin,android,java)