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)