kotlin学习二(今日头条)

1. 主类

package com.example.todayheadline.guolin

import android.content.Context
import android.content.Intent
import android.widget.Toast
import com.example.todayheadline.base.BaseApp
import kotlin.concurrent.thread
import kotlin.math.ln
import kotlin.math.max
import kotlin.reflect.KProperty

fun main() {

    //基本使用
    println("${testMax(6, 19)}")
    //基本使用-if
    println("${testMax2(19, 6)}")
    //基本使用-when
    println("${textScope("李四")}")
    //基本用法-when 类型匹配:使用is 代替java中的instanceOf
    textScope(9)
    //基本用法-for循环(for-in)
    var range = 0..10 // ..代表双闭合区间(升序)
    var rangeUntil = 0 until 10 //until 代表左闭右开,例如数组下标0-9 就可以使用until
    for (i in range) {
        print("${i}--")
    }
    println("")
    for (i in rangeUntil) {
        print("${i}--")
    }
    println("")
    //使用step跳过某些元素
    for (i in 0 until 10 step 3) {
        print("${i}--")
    }
    println("")
    //使用downTo实现降序区间
    for (i in 10 downTo 0 step 2) {
        print("${i}--")
    }
    println("")
    //基本用法-继承类使用 ": SuperCalss()",父类必须使用open关键字,因为kotlin默认创建类是不可以被继承的(相当于使用类final关键字,抽象类除外)
    //每个类里面只能有一个主构造函数,多个次构造函数(使用constructor关键字定义)
    //每个类默认都有一个不带参数的主构造函数,主构造函数的特点是没构造体,直接在类名后面声明即可,所以要想编写业务逻辑的话,kotlin提供类init结构体给我们使用
    //主构造函数使用val或var声明的参数会自动成为该类的字段,但name,age不能声明成val或var,因为父类Person里面也有这两个字段,会造成字段冲突,不声明val或var,就可以只在主构造函数里面使用
    //次构造函数必须调用主构造函数(直接间接都可)
    var student = Student("2012", "100", "LS", "27")
    var student2 = Student()
    var student3 = Student("张三", "30")
    //基本用法-实现接口使用 ":Interface ",不需要使用(),因为接口没有构造函数
    student.readBooks()
    student.doHomeWork()//接口中的函数有函数体的话,子类实现该接口时,不必强制重写该函数
    //基本用法-修饰符
    //public  默认项,任何地方都可见,可以不写,对应java的public 不过java默认项是default(同一包路径下的类可见)
    //private 只对类内部部可见,同java一样
    //protected  只对当前类以及子类可见  java中对应当前类以及子类以及同一包路径下的类可见,主要是针对子类使用
    //internal 只对同一模块的类可见,kotlin抛弃了default(只对同一包路径下的类可见)
    //总结: kotlin把默认项改为public,并抛弃了default,使用了internal,protected也抛弃了对同一包路径下的类可见这个选项

    //基本用法-数据类 使用data关键字,会自动生成equals(),toString(),hashCode()
    var cellPhone = CellPhone("123456", "100")
    var cellPhone2 = CellPhone("123456", "100")
//    cellPhone.mobile = "7890"
//    println("${cellPhone.mobile}")
    println("${cellPhone == cellPhone2}")
    //基本用法-单例模式 使用object关键字
    Singleton.singletonTest()

    //Lambda编程
    //定义一个集合List  另外:Set用法类似,也是使用setOf,mutableSetOf
    //第一种方法
    val list = ArrayList<String>()
    list.add("Apple")
    list.add("Banana")
    //第二种方法,不过listOf创建的集合是不可变的,只能读取,不能添加,修改或删除
    val list2 = listOf("Apple", "Banana")
    //第三种方法 集合可变,使用mutableListOf关键字.可读写
    val list3 = mutableListOf("Apple", "Banana")
    list3.add("Orange")
    //遍历集合
    for (fruit in list3) {
        print("list--${fruit}--")
    }
    println("")
    //定义Map集合,无序
    val map = HashMap<String, Int>()
    //第一种写法,不建议,kotlin不建议用put,get来进行数据存储和操作
    map.put("Apple", 1)
    map.put("Banana", 2)
    //第二种写法,使用类似数组下标的写法
    map["Orange"] = 3
    println("${map["Orange"]}")
    //第三种方法 只读集合
    val map2 = mapOf("Apple" to 1, "Banana" to 2)
    //第四种方法 可读写集合
    val map3 = mutableMapOf("Apple" to 1, "Banana" to 2)
    map3["Orange"] = 3
    //遍历集合
    //第一种方法
    for (fruit in map) {
        print("map--${fruit.key}--${fruit.value}--")
    }
    println("")
    //第二种方法 推荐
    for ((fruit, num) in map3) {
        print("map2--${fruit}--${num}--")
    }
    println("")
    //Lambda表达式
    //基本语法 {参数1:参数类型,参数2:参数类型 -> 函数体}  函数体的最后一行代码作为lambda表达式的返回值
    //需求:求出list中,单词长度最长的水果
    //标准写法1
    val lambda = { fruit: String -> fruit.length }
    val maxFruit = list3.maxBy(lambda)
    //标准写法2
    val maxFruit2 = list3.maxBy({ fruit: String -> fruit.length })
    //简化写法1,lambda参数是函数的最后一个参数时,可以把lambda表达式移到()外面
    val maxFruit3 = list3.maxBy() { fruit: String -> fruit.length }
    //简化写法3,lambda参数是函数的唯一一个参数时,可以省略()
    val maxFruit4 = list3.maxBy { fruit: String -> fruit.length }
    //简化写法4,因为kotlin有类型推导机制,所以可以省略参数类型
    val maxFruit5 = list3.maxBy { fruit -> fruit.length }
    //简化写法5,当lambda表达式只有一个参数时,可以不声明参数名,直接用it代替
    val maxFruit6 = list3.maxBy { it.length }
    println("${maxFruit6}")
    //小需求2,把list3的数据都改为大写,主要使用map函数,他可以把每个元素映射成另外的值,例如都变为大写
    val list4 = list3.map { it.toUpperCase() }
    //小需求3,过滤掉单词长度大于5的,且最后转化为小写,主要使用filter和map
    val list5 = list4.filter { it.length <= 5 }.map { it.toLowerCase() }
    //遍历集合
    for (fruit in list5) {
        print("list5--${fruit}--")
    }
    println("")
    //小需求4 判断是否满足指定条件,主要使用any和all函数
    list3.add("Pear")
    val anyResult = list3.any { it.length <= 5 }//any至少有一个元素满足条件
    val allResult = list3.all { it.length <= 5 }//all所有元素都要满足条件
    println("list3中单词长度小于等于5--${anyResult}--${allResult}")
    //java函数式API的使用(只适用于kotlin调用java方法,并且这个单抽象方法接口是java语言定义的(只针对接口中有一个待实现方法) eg:RunnableOnClickListener)
    //kotlin有专门的高阶函数来实现自定义函数式API功能,不需要像java一样借助单抽象方法接口来实现
    //匿名内部类的写法,因为kotlin完全舍弃了new关键字,所以匿名内部类改用了object关键字
    Thread(object : Runnable {
        override fun run() {
            println("我开启来个线程${Thread.currentThread().name}")
        }
    }).start()
    //简写如下
    Thread(Runnable {
        println("我又开启来个线程${Thread.currentThread().name}")
    }).start()
    //终极写法
    Thread {
        println("我又又开启来个线程${Thread.currentThread().name}")
    }.start()

    //空指针  Kotlin默认所有的参数和变量都不能为空
    //? 表示参数或变量可以为空
    //?.相当于对象不为空时正常调用,为空的话就什么也不做,相当于if(Class !=null)
    //?: 相当于 val c = a?:b  如果a表达式不为空,就返回a,否则就返回b表达式
    //!! 表示我确信这个对象一定不为空,不用帮我做非空判断了,如果有问题直接就抛空指针异常了
    //标准函数-let函数,将原始调用对象作为参数传递到lambda表达式中 object.let{object-> 具体逻辑} let函数解决了每次都要重新判空
    getStudy(null)
    println("${getTextLength(null)}")
    getName()
    //使用let函数
    getStudy(object : Study {
        override fun readBooks() {
        }
    })
    //使用小技巧
    //当表达式只有一个变量时,可以直接使用$变量名,省略掉{}
    var test = "我是字符串内嵌表达式"
    println("$test")
    //函数的参数默认值
    //可以不传name参数
    printParams(30)
    //当年龄为默认值
    printParams2(name = "ls")
    //更多应用
    var student4 = Student2("2012", "100", "LS", "27")
    var student5 = Student2()
    var student6 = Student2(name = "张三", age = "30")
    //标准函数-with函数,主要用于简化连续调用同一个对象的多个方法 with(object){lambda表达式} lambda表达式的最后一行是返回值
    var withTest = with(StringBuilder()) {
        append("使用with我来拼接水果字符串了\n")
        for (i in list5) {
            append("水果是$i\n")
        }
        append("拼接水果字符串结束")
        toString()
    }
    println("$withTest")
    //标准函数-run函数,和with函数使用场景类似 但是它不能直接调用,而是使用某个对象来调用,它只接收一个lambda参数  Object.run{ lambda表达式} lambda表达式的最后一行是返回值
    var runTest = StringBuilder().run {
        append("使用run我来拼接水果字符串了\n")
        for (i in list5) {
            append("水果是$i\n")
        }
        append("拼接水果字符串结束")
        toString()
    }
    println("$runTest")
    //标准函数-apply,跟run函数有些类似,必须要有对象来调用  它只接收一个lambda参数,并且没法指定返回值,返回值只能是对象本身  Object.apply{ lambda表达式}
    //Intent传递参数时可以使用apply函数
    var applyTest = StringBuilder().apply {
        append("使用apply我来拼接水果字符串了\n")
        for (i in list5) {
            append("水果是$i\n")
        }
        append("拼接水果字符串结束")
    }
    println("${applyTest.toString()}")
    //定义静态方法-companion object
    StaticTest().testFun()
    //companion object实际是在StaticTest内部创建了一个伴生类,而testFun2()就是伴生类里面的实例方法
    //StaticTest类中只能存在一个伴生类, StaticTest.testFun2()本质是调用了伴生类的testFun2()
    //companion object并不是真正的静态方法,只是语法形势模仿了静态方法的调用方式,
    //java调用Kotlin方法时,Koltin只要在方法上使用@JvmStatic注解,编译器就会把该方法编译成真正的静态方法  参考TestStatic类的调用
    StaticTest.testFun2()
    //顶层方法-指没有定义在任何类中的方法,如:main方法, kotlin编译器会把顶层方法全部编译成静态方法,所有顶层方法可以在任意位置被调用
    // eg:  SecondeActivity类中的 printParams2(name = "我是顶层方法")
    //java要想调用顶层方法的话,kotlin编译器会根据Guolin.kt来生成一个GuolinKt.java类,Java直接调用GuoLinKt.printParams2(**);即可 代码在TestStatic.java类中

    //lateinit关键字使用:延迟初始化
    //它会告诉kotlin编译器,我会在晚些时候对这个变量进行初始化,这样就避免一开始就得将它赋值为空了
    nolateinit?.test()  //如果不使用lateinit的话,定义的时候需要设置可以为空 ?  调用它的方法时,还得进行非空判断 ?.
    if (!::lateinitTest.isInitialized) {// ::lateinitTest.isInitialized判断是否已经完成初始化了,加!代表取反,没有进行初始化
        lateinitTest = LateInitClass()//这样判断也避免重复进行初始化操作
    }
    lateinitTest.test()//而使用lateinit的话,就可以不用非空判断,但你得确保在调用它的方法之前初始化了  使用场景:adapter的定义就可以使用lateinit关键字
    //密封类-sealed class  密封类及密封类的子类必须要定义在同一个文件中,且不能嵌套到其他类中
    //密封类的作用就是when条件中不用写else,而且使用when必须把密封类的子类都列举出来,不然会编译报错
    //密封类作用类似于枚举,不过枚举的话一种状态只能定义一个实例,而密封类可以定义一个实例(object),也可以定义多个实例(class)
    //使用场景:adapter有不同的布局文件时,使用when来判断使用哪个布局
    getSealed(SealedTestClass1())
    getSealed(SealedTestClass2)
     //扩展函数 eg:在String类中自己扩展一个获取字符串中字母的个数 (在java语言中,String是个final类,任何类不能继承它,它的api就那几个,所以kotlin有扩展函数,使api更丰富)
    //扩展函数不是在类的内部定义了新的方法,而是使用装饰模式,对类实例进行操作和包装,相当于定义了一个工具类里面的方法。通过java反编译可以看到,其实就是定义了一个方法,第一个参数是这个类本身的实例
    //this代表这个对象实例,就是.之前的对象实例
    var letterCount: Int = "ette4567fdffdfd".letterCount()
    println("String扩展函数--字母的个数为$letterCount")
    //运算符重载 使用operator关键字,eg:把字符串重复n次  *对应的实际函数是times 参考书籍P238
    var repeatStr = "23fj" * (3..10).random()
    println("String运算符重载--$repeatStr")
    //高阶函数(如果一个函数的参数为另一个函数或者返回值为另一个函数,这个函数就叫高阶函数),一般使用lambda表达式来调用高阶函数
    //基本语法-(Int,String)-> Unit(或者Int/String)  若无参数()->Unit
    //eg:定义一个高阶函数,调用时传入lambda表达式,计算加法和减法
    var highAdd = highFun(10, 2) { a, b -> a + b }
    var highSub = highFun(10, 2) { a: Int, b: Int -> a - b }
    println("$highAdd--$highSub")
    //lambda在底层(.class字节码)被转换成了匿名内部类的写法(new Function(){ invoke()})
    //这样的话每调用一次lambda表达式,就会创建一个匿名内部类,就会造成额外的内存和性能开销
    //kotlin提供了内联函数 只要在高阶函数前添加inline关键字即可,它会在编译时把内联函数的代码替换到调用它的地方
    // 第一步:把lambda表达式的代码a+b替换到函数类型参数调用的位置funTest()
    // 第二步:把内联函数的代码a+b全部替换到函数调用的地方highAdd = a+b
    //高阶函数学习也可以参考FileActivity类中的sp简写
    //泛型
    //泛型类(class A<T> (){})
    var myClass = Myclass<Int>()
    var myMethod = myClass.method(123)
    println("Int泛型类-$myMethod")
    //泛型方法(fun <T> B(){})
    var myMethod2 = myClass.method2("789")
    println("String泛型方法-$myMethod2")
    //使用泛型写高阶函数(build是扩展函数,StringBuffer可以换成任何对象)
    var stringbuffer = StringBuffer().build {
        append("我是泛型高阶函数")
    }
    println("$stringbuffer")
    //类委托和委托属性 使用by关键字
    //类委托本质是把一个类的具体实现交给另一个类去实现(使用类委托就不用重写set的所有方法了)
    //委托属性本质是把一个属性的具体实现交给另一个类去实现
    var myset = Myset<Int>(HashSet())
    myset.hello()
    myset.p
    //属性委托我们一般常用 内置的lazy函数:var 变量名 by lazy{lambda表达式,最后一行为返回值}(它是懒加载/延迟加载,只有变量被首次调用的时候才会执行)

    //infix函数 它只是把函数的调用语法调整了一下(语法糖,类似于英语的语法,使代码更可读)  eg A to B 其实是 A.to(B)
    //with是扩展A的扩展函数,如果不加infix的话,就得使用"Apple" .with(1)  而加了infix,直接"Apple" with 1 就可以了
    //infix使用限制: 不能是顶层函数,必须要函数定义到某个类中(eg:A),另外infix函数只能接收一个参数,参数类型无限制
    var map4 = mapOf("Apple" with 1)
    println("$map4")
    //开启线程的方法:thread是kotlin内置的顶层函数,不用使用.start()开启线程
    thread {
        println(Thread.currentThread().name)
    }

    //泛型实化(这样就不需要使用泛型擦除了),它允许我们在泛型函数中获取到泛型的实际类型
    // 具备条件:1,函数是内联函数(内联函数的代码会在编译时自动替换到调用它的地方) 2.声明泛型的地方加上
    //案例: a is b ,a::class.java
    var stringType  = getType<String>()
    var intType  = getType<Int>()
    println("$stringType---$intType")
    //使用vararg关键字,可以接收多个同等类型的参数
    println("使用vararg比大小---${max(2,4,7,29,5)}")
    //在java中,所有类型的数字都是可以比较的,但是需要实现Comparable接口
    //如果我们想比较所有的类型的大小的话,就可以使用泛型来定义,不过要注意这个泛型需要实现Comparable,才可以比较
    println("使用vararg比大小--${maxAll(1.2,3.9,4.5,1.5)}")

}
/**
 * 在String中封装了Toast方法
 */
fun String.showToast(during:Int = Toast.LENGTH_SHORT){
    //this代表String自己
    Toast.makeText(BaseApp.myApplicationContext,this,during).show()
}
/**
 * 在String中封装了Toast方法
 */
fun String.showToast(context: Context,during:Int = Toast.LENGTH_SHORT){
    //this代表String自己
    Toast.makeText(context,this,during).show()
}
/**
 * 在Int中封装了Toast方法
 */
fun Int.showIntToast(context: Context,during:Int = Toast.LENGTH_SHORT){
    //this代表Int自己
    Toast.makeText(context,this,during).show()
}

/**
 * 使用vararg比较Int类型的大小
 */
fun max(vararg number:Int ):Int{
    var maxNum = Int.MIN_VALUE
    for(num in number){
        maxNum = max(maxNum,num)
    }
    return  maxNum
}
/**
 * 使用Comparabl来比较各种类型的数字
 */
fun <T:Comparable<T>>maxAll(vararg number:T ):T{
    var maxNum = number[0]
    for(num in number){
       if(maxNum<num){
           maxNum = num
       }
    }
    return  maxNum
}


//高阶函数+泛型实例
inline fun  <reified T> startActivity(context: Context,block :Intent.()->Unit){
    val intent = Intent(context,T::class.java)
    intent.block()
    context.startActivity(intent)
}

inline fun <reified T> getType() = T::class.java

infix fun <A, B> A.with(b: B): Pair<A, B> = Pair(this, b)

class Myset<T>(hashSet: HashSet<T>) : Set<T> by hashSet {
    var p by DelegateClass()
    fun hello() {
        println("我是类委托")
    }
}

class DelegateClass() {
    var delegateClass: Any? = null
    operator fun <T> getValue(myset: Myset<T>, property: KProperty<*>): Any? {
        println("我是委托属性的get方法")
        return delegateClass
    }

    operator fun <T> setValue(myset: Myset<T>, property: KProperty<*>, any: Any?) {
        println("我是委托属性的set方法")
        delegateClass = any
    }

}

fun <T> T.build(t: T.() -> T): T {
    return t()
}


//T是约定俗成的一种写法,你可以定义成其他字母
class Myclass<T>() {
    fun method(t: T): T {
        return t
    }

    fun <T> method2(t: T): T {
        return t
    }
}

inline fun highFun(num1: Int, num2: Int, funTest: (Int, Int) -> Int): Int {
    return funTest(num1, num2)
}


sealed class SealedClass

//密封类及密封类的子类必须要定义在同一个文件中,且不能嵌套到其他类中
class SealedTestClass1 : SealedClass()
object SealedTestClass2 : SealedClass()


fun getSealed(sealedClass: SealedClass) = when (sealedClass) {
    is SealedTestClass1 -> println("我是密封子类1")
    is SealedTestClass2 -> println("我是密封子类2")
}

class LateInitClass {
    fun test() {
        println("我用来测试lateinit关键字的")
    }
}

lateinit var lateinitTest: LateInitClass
var nolateinit: LateInitClass? = null

class StaticTest {
    fun testFun() {

    }

    companion object {
        @JvmStatic
        fun testFun2() {
        }
    }
}

fun printParams(age: Int, name: String = "yjl") {
    println("$age--$name")

}

fun printParams2(age: Int = 10, name: String) {
    println("$age--$name")

}

var name: String? = "LLLLLSSSSS"
fun getName() {
    println("${name!!.toLowerCase()}")
}

fun getTextLength(str: String?) = str?.length ?: 0

fun testMax(a: Int, b: Int) = max(a, b)

//kotlin的if语句是有返回值的,返回值为if语句中的最后一行代码
//另外kotlin可以类型推倒,所以我们不需要定义testMax2的返回值为Int,直接使用"="代表了返回值
fun testMax2(a: Int, b: Int) = if (a > b) a else b

//when有返回值使用"="
fun textScope(name: String) =
    when (name) {
        "张三" -> 98
        "李四" -> 30
        else -> 0
    }

//when无返回值使用"{}"
fun textScope(num: Number) {
    when (num) {
        is Int -> println("我是Int类型")
        is Double -> println("我是Double类型")
        else -> print("类型错误")
    }

}

open class Person(val name: String, val age: String) {
    //    var name = ""
//    var sex = ""
//    init {
//        println("${name}--${age}")
//    }
}

interface Study {
    fun readBooks()
    fun doHomeWork() {
        println("我有函数体,我可以不被强制实现")
    }
}

class Student(val sno: String, val gradle: String, name: String, age: String) : Person(name, age),
    Study {
//    var sno=""//班级
//    var grade = ""//成绩

    init {
        println("${sno}--${gradle}--${name}--${age}")
    }

    //用来测试主构造函数中参数使用var或val或不使用时的区别
    fun eat() {
        println("${sno}--${gradle}--${name}--${age}")
    }

    constructor(name: String, age: String) : this("2020", "99", name, age) {

    }

    constructor() : this("李四", "28") {

    }

    override fun readBooks() {
        println("${sno}--${gradle}--${name}--${age}喜欢读书")
    }
}

class Student2(
    val sno: String = "2020",
    val gradle: String = "99",
    name: String = "李四",
    age: String = "28"
) : Person(name, age),
    Study {

    init {
        println("${sno}--${gradle}--${name}--${age}")
    }


    override fun readBooks() {
        println("${sno}--${gradle}--${name}--${age}喜欢读书")
    }
}

data class CellPhone(var mobile: String, var price: String)

object Singleton {
    fun singletonTest() {
        println("我是一个单例类")
    }

}

fun getStudy(study: Study?) {
    //?.相当于对象不为空时正常调用,为空的话就什么也不做,相当于if(Class !=null)
    study?.doHomeWork()
    study?.readBooks()
    //上述写法相当于两次进行if判断,可以使用let函数来判断
    //?.表示study为空的话就不进行操作,如果不为空则进行let函数
    study?.let {
        it.doHomeWork()
        it.readBooks()
    }
}



2.其他类(扩展方法)

package com.example.todayheadline.guolin

//扩展函数(原本没有的)
fun String.letterCount():Int{
    var count = 0
    for(i in this){
        if(i.isLetter()){
            count++
        }
    }
    return count
}

//运算符重载(在原本已有的运算符方法上进行重载) 重载使用oper
//operator fun String.times(num:Int):String {
//    var stringBuffer = StringBuffer()
//    repeat(num){
//        stringBuffer.append(this)
//    }
//   return stringBuffer.toString()
//}
//上面方法可以简写为
operator fun  String.times(num: Int) :String= this.repeat(num)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值