郑州高端网站建设怎么样,iis创建网站,手机上使用wordpress,编程线下培训机构前言#xff1a;
通过前面几篇文章#xff0c;我们已基本掌握kotlin的基本写法与使用#xff0c;但是在开发过程中#xff0c;以及一些开源的API还是会出现大家模式的高阶玩法以及问题#xff0c;如何避免#xff0c;接下来讲解针对原来的文章进行一些扩展#xff0c;解…前言
通过前面几篇文章我们已基本掌握kotlin的基本写法与使用但是在开发过程中以及一些开源的API还是会出现大家模式的高阶玩法以及问题如何避免接下来讲解针对原来的文章进行一些扩展解决大家在工作中遇到的问题如何去解决
如果还有人不了解kotlin可以查看我的基础篇kotlin。
Android kotlin在实战过程问题总结与开发技巧详解_kotlin 同步锁_蜗牛、Z的博客-CSDN博客
Kotlin语法详解与实践教程区分JAVA以及如何闭坑_kotlin mapof可以嵌套 to_蜗牛、Z的博客-CSDN博客 1. lazy
lazy是kotlin中的懒加载这种写法在很多场景中都有懒加载并不是立刻在内存中申请而是通过lazy()调用才会
lazy的只能是常量用val修饰变量是全局也可以是局部变量 通过这个lazy{}可以看出无论你在最上面调用什么默认值都是最后一个。通过IDE可以提示看到ccc ^lazy,这就是懒加载的值。这个值也可以通过方法获取 fun a() {val aa: String by lazy {MyLog.log(val aa:String by lazy start)aaaabbbbb()}MyLog.log(aa)MyLog.log(aa)
}fun b(): String {return aaaaa
}
一旦懒加载对象被调用过后期在调用将只能取到值不会再处理其他的lazy{} 方法体。 注意lazy和lateinit无法一起使用。lazy修饰val变量lateinit修饰的是var变量。 2.lateinit
也是懒加载机制对象是var的格式。但是这个变量需要是全局变量不能是局部变量。
lateinit var string: String
lateinit var people: People
fun c() {people People()people.name zhangshanstring aaaaMyLog.log(people.name!!)val child: People by lazy{People()}} 3.如何避免参数加!!可为空符号
kotlin的变量如果不是懒加载修饰那么你在申请的时候就要给出变量值即默认值
常见写法:
var name:String?null,表示当前对象是null
var title:Stringtitle,表示已默认值、
但是如果你申请为null那么你在赋值的时候这个参数就要在多个地方加上!!,否则会报错。这种写法很烦人。
如何避免
使用懒加载机制通过上面两个懒加载可以避免这种问题的参数。否则你在任何地方进程参数传递都会被判为null。
4.类的扩展函数
kotlin支持在现有的类中动态扩展函数格式就是类对象.函数名可以支持参数和返回值扩展的函数和正常函数一样使用
以下是对String扩展一个两个方法分别是log和size() fun String.log(log:String) {MyLog.log(log)
}fun String.size(): Int {return toString().length
}
5.扩展属性、扩展变量
kotlin支持动态新增变量格式就类名.变量名:类型
这种写法可以无限的扩展你需要的变量扩展变量一样只能在最外围不能在方法体中定义
扩展属性需要额外重写get()方法且不能申请直接赋值
val String.defaulename: Stringget() {return defaulename}fun e() {val nameMyLog.log(name.defaulename)
}这里需要重写get(),给出默认值。 6.apply与let与also
apply与alse是链式设置返回的是当前对象let返回值是unit
apply、also闭包的返回值都是this前者apply接受的闭包类型调用者的扩展函数后者接受的闭包类型为 入参为调用者类型的函数also、apply非常适合对同一个对象连续操作的链式调用run、let闭包的返回值为最后一行非赋值代码前者run接受的闭包类型调用者的扩展函数后者接受的闭包类型为 入参为调用者类型的函数run、let非常适合上一个操作返回值作用于下一个操作的调用
我们经常会使用如下写法
if(people!null){peope.namezhangshan} apply写法
people?.apply{it-it.namezhangsnan} 7.类的初始化模块init
kotlin中没有static{}模块是通过init{}替代了static。如果需要提前初始化的可以放在init中常见的so库加载可以放到init中
class Example {init {//init module}
} 8 .接口多继承方法名冲突 如果接口中有两个相同的方法名在java中是需要修改一个的。在kotlin中就可以避免这种。只要在继承方法中指向各个类的即可。 class TestInterface : IntFacA, IntFacB {override fun log() {superIntFacA.log()superIntFacB.log()}override fun info() {}
}9.internal 介绍
internal 属于修饰和provite、public、protect一样
private 意味着只在这个类内部包含其所有成员可见protected—— 和 private一样 在子类中可见。internal —— 能见到类声明的 本模块内 的任何客户端都可见其 internal 成员public —— 能见到类声明的任何客户端都可见其 public 成员。10.数据类data
将类对象修饰成data需要的变量直接在构造器中申明不需要方法体 data class User(val name: String , val age: Int 0) 11.密封类sealed
密封类用来表示受限的类继承结构当一个值为有限几种的类型、而不能有任何其他类型时。在某种意义上他们是枚举类的扩展枚举类型的值集合也是受限的但每个枚举常量只存在一个实例而密封类的一个子类可以有可包含状态的多个实例。
要声明一个密封类需要在类名前面添加 sealed 修饰符。虽然密封类也可以有子类但是所有子类都必须在与密封类自身相同的文件中声明
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
一个密封类是自身抽象的它不能直接实例化并可以有抽象abstract成员。
密封类不允许有非-private 构造函数其构造函数默认为 private。
fun eval(expr: Expr): Double when(expr) {is Const - expr.numberis Sum - eval(expr.e1) eval(expr.e2)NotANumber - Double.NaN// 不再需要 else 子句因为我们已经覆盖了所有的情况
} 类似接口继承通过泛型来判断
12.泛型通配符类型参数in 和 out
在开发过程中泛型的利用可以很好的解决代码臃肿问题也能很好的进行解耦。在kotlin中泛型的使用有普通的也有通配符限定
1.正常泛型
interface FactoryT {fun getEntity(): T
}class Room : FactoryString {override fun getEntity(): String {return String info}
}
这种是我们最常见的泛型通过泛型可以很好的获取不同的对象类型 2通配符 out和in
java中也有通配符? extend E,? extends E 表示此方法接受 E 或者 E 的 一些子类型对象的集合而不仅仅是 E 自身。简而言之带 extends 限定上界的通配符类型使得类型是协变的。
还有一种? super String表示只接收String类型。
在 kotlin 语言中out 表示协变in 表示逆变
kotlin 中的 “out T” 等同于 Java 的 “extends T”
kotlin 中的 “in T” 等同于 Java 的 “super T”
也就是上线和下线。 13.嵌套类
内部类可以直接使用就是通过最外层类引用即可也可以单独使用
class Student {lateinit var name: Stringclass Classmate {lateinit var name: String}
}fun main() {var mateStudent.Classmate()mate.namezhangsanMyLog.log(mate.name)
} 14.内部类inner
标记为 inner 的嵌套类能够访问其外部类的成员。内部类会带有一个对外部类的对象的引用。
class Student {lateinit var name: Stringinner class Classmate {lateinit var name: String}
}fun main() {var mateStudent().Classmate()mate.namezhangsanMyLog.log(mate.name)
} 15.匿名内部类
使用对象表达式创建匿名内部类实例
window.addMouseListener(object : MouseAdapter() {override fun mouseClicked(e: MouseEvent) { …… }override fun mouseEntered(e: MouseEvent) { …… }
}) 接口的监听
1.lambda表达式 2.object匿名内部类 view.setOnClickListener(object :View.OnClickListener{override fun onClick(v: View?) {}})
16.枚举类
枚举类的最基本的用法是实现类型安全的枚举kotlin枚举有一下几种
1.支持匿名类
2.支持参数扩展
enum class MyEmu {ONE{var size123},TWO,THREE
}enum class MyEmu1(tag:Int) {ONE(1),TWO(2),THREE(3)
}
如果你想弄那么复杂就当普通枚举使用 3、枚举类型新增接口
enum class MyEmu :MyInfo{ONE{override fun log() {TODO(Not yet implemented)}},TWO{override fun log() {TODO(Not yet implemented)}}
}
interface MyInfo {fun log();
}
如果枚举类型实现了接口那么枚举中的任何元素的都变成了匿名内部类都要实现接口的方法 17.open超类
如果一个类无法被继承就可以将该类通过open修饰如果类中的变量无法使用也可以通过open修饰。 open class MyNAME {open var name
}18.静态类object
静态类是通过object修饰该类里面的所有变量和方法都是静态。且不在需要class修饰
object staticName {var name fun log() {}
}19.伴生对象
伴随对象是在普通类中进行扩展一个对象出来的该伴生对象的成员可通过只使用类名作为限定符来调用
class BanshengObject {companion object {var name fun log() {}}
}fun main() {BanshengObject.log()BanshengObject.name
}
请注意即使伴生对象的成员看起来像其他语言的静态成员在运行时他们仍然是真实对象的实例成员
如果使用 JvmStatic 注解你可以将伴生对象的成员生成为真正的静态方法和字段 20.类型别名typealias 类型别名为现有类型提供替代名称。 如果类型名称太长你可以另外引入较短的名称并使用新的名称替代原类型名。
class Alias {inner class MyStudent(name:String)
}typealias Alias_MyStudent Alias.MyStudenttypealias SetStringSetString
typealias MyHandler (Int, String, Any)-Voidfun main() {var m:MyHandler}
这个别名使用在kotlin有很多kotlin对java的api进行封装就是通过别名完成。也可以支持方法的别名 21.内联类
内联类必须含有唯一的一个属性在主构造函数中初始化。在运行时将使用这个唯一属性来表示内联类的实例
inline class InlinerBean(val title:String) {val length: Intget() title.length
}fun main() {val beanInlinerBean(this is inline class)}
// 不存在 InlinerBean 类的真实实例对象 // 在运行时InlinerBean 仅仅包含 String val beanInlinerBean(this is inline class) 同时内联是为了解决额外的堆内存分配问题。
内联类支持普通类中的一些功能。特别是内联类可以声明属性与函数
内联函数有且仅有一个构造参数
内联函数也可以当普通函数使用可以实现接口 22.委托by
委托模式已经证明是实现继承的一个很好的替代方式 而 Kotlin 可以零样板代码地原生支持它
interface Base {val message: Stringfun print()
}class BaseImpl(val x: Int) : Base {override val message BaseImpl: x $xoverride fun print() { println(message) }
}class Derived(b: Base) : Base by b {// 在 b 的 print 实现中不会访问到这个属性override val message Message of Derived
}fun main() {val b BaseImpl(10)val derived Derived(b)derived.print()println(derived.message)
}
注意以这种方式重写的成员不会在委托对象的成员中调用 委托对象的成员只能访问其自身对接口成员实现。 23.委托属性 有一些常见的属性类型虽然我们可以在每次需要的时候手动实现它们 但是如果能够为大家把他们只实现一次并放入一个库会更好。例如包括
延迟属性lazy properties: 其值只在首次访问时计算可观察属性observable properties: 监听器会收到有关此属性变更的通知把多个属性储存在一个映射map中而不是每个存在单独的字段中。
为了涵盖这些以及其他情况Kotlin 支持 委托属性:
class Example {var p: String by Delegate()
}
语法是 val/var 属性名: 类型 by 表达式。在 by 后面的表达式是该 委托 因为属性对应的 get()与 set()会被委托给它的 getValue() 与 setValue() 方法。 属性的委托不必实现任何的接口但是需要提供一个 getValue() 函数与 setValue()——对于 var 属性 class Delegate {operator fun getValue(thisRef: Any?, property: KProperty*): String {return $thisRef, thank you for delegating ${property.name} to me!}operator fun setValue(thisRef: Any?, property: KProperty*, value: String) {println($value has been assigned to ${property.name} in $thisRef.)}
}
分析
by是ReadOnlyProperty的连接
public fun interface ReadOnlyPropertyin T, out V {/*** Returns the value of the property for the given object.* param thisRef the object for which the value is requested.* param property the metadata for the property.* return the property value.*/public operator fun getValue(thisRef: T, property: KProperty*): V
}
所有我们在属性委托时需要实现 public operator fun getValue(thisRef: T, property: KProperty*): V
所以定义属性委托必须要实现接口ReadOnlyProperty 实战
在jetpack的datastore中也提供了属性委托。 val Context.dataStore: DataStorePreferences by preferencesDataStore(my_datastore,produceMigrations { it -listOf(SharedPreferencesMigration(it, sp_test))}) 24.Lambda 表达式
lambda 表达式与匿名函数是“函数字面值”即未声明的函数 但立即做为表达式传递
max(strings, { a, b - a.length b.length })
函数 max 是一个高阶函数它接受一个函数作为第二个参数。 其第二个参数是一个表达式它本身是一个函数如下
fun compare(a: String, b: String): Boolean a.length b.length
Lambda 表达式语法
Lambda 表达式的完整语法形式如下
val sum: (Int, Int) - Int { x: Int, y: Int - x y }
lambda 表达式总是括在花括号中 完整语法形式的参数声明放在花括号内并有可选的类型标注 函数体跟在一个 - 符号之后
val sum { x: Int, y: Int - x y } val sum: (Int, Int) - Int { x: Int, y: Int - x y }sum(2,3)val add { x: Int, y: Int - x y }add(2,3) 从 lambda 表达式中返回一个值 ints.filter {val shouldFilter it 0 shouldFilter
}ints.filter {val shouldFilter it 0 returnfilter shouldFilter
} 25.构造集合
在开发过程中发现使用集合都需要指定泛型还有一种可以指定一个默认值后面所有的集合都是这种
val numbersSet setOf(one, two, three, four)
val emptySet mutableSetOfString() 26.空集合
还有用于创建没有任何元素的集合的函数emptyList()、emptySet() 与 emptyMap()。 创建空集合时应指定集合将包含的元素类型。 27.具体类型构造函数
要创建具体类型的集合例如 ArrayList 或 LinkedList可以使用这些类型的构造函数。 类似的构造函数对于 Set 与 Map 的各实现中均有提供
val linkedList LinkedListString(listOf(one, two, three))
val presizedSet HashSetInt(32) 28.集合加减法
我们正常使用逻辑都是对于变量运算但是在kotlin中的集合也可以使用 val plusList numbers fiveval minusList numbers - listOf(three, four)val minusList2 numbers - threeprintln(plusList)println(minusList)println(minusList2) 29.集合数据分组
Kotlin 标准库提供用于对集合元素进行分组的扩展函数。 基本函数 groupBy() 使用一个 lambda 函数并返回一个 Map。 在此 Map 中每个键都是 lambda 结果而对应的值是返回此结果的元素 List。 例如可以使用此函数将 String 列表按首字母分组。 val numbers listOf(one, two, three, four, five)println(numbers.groupBy { it.first().toUpperCase() })30.协程
Kotlin 是一门仅在标准库中提供最基本底层 API 以便各种其他库能够利用协程的语言。与许多其他具有类似功能的语言不同async 与 await 在 Kotlin 中并不是关键字甚至都不是标准库的一部分。此外Kotlin 的 挂起函数 概念为异步操作提供了比 future 与 promise 更安全、更不易出错的抽象。
kotlinx.coroutines 是由 JetBrains 开发的功能丰富的协程库。它包含本指南中涵盖的很多启用高级协程的原语包括 launch、 async 等等。 在kotlin中suspend修饰的方法叫着协程。目前针对协程有一下几种
1.同步runBlocking
2.异步kotlinx-coroutines-core库 代码库
implementation org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1 class MySuspendMain {suspend fun info() {}
}fun main() {GlobalScope.launch { // 在后台启动一个新的协程并继续delay(1000L) // 非阻塞的等待 1 秒钟默认时间单位是毫秒println(World!) // 在延迟后打印输出}println(Hello,) // 协程已在等待时主线程还在继续Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活runBlocking {val mainsMySuspendMain()mains.info()}} 由于kotlinx-coroutines-core库很大我会出一篇详细的文章讲解。