之前没用过,记一下。
val number = 148
if(number in 10..59) {
} else if (number in 0..9) {
}
// 四舍五入
64.465165156.toInt()
65.123132131.roundToInt()
// r的类型: String
val r = "%.3f".format(65.123456)
// r = 65.123 保留3位
kotlin 中各个基础类型对比java看起来是自动装箱之后的java类型,比如double ,kt中是Double
实际上经过编译之后还是double基础类型。
在项目当中我们会大量使用函数作为参数进行传参,就是使用lambda作为参数,需要声明成内联
fun test(sername: String, reapnseResult:(String,Int) -> Unit) {}
类型上面这样的函数,内部会生成多个对象来完成lambda的调用(性能损耗)
而加了inline 关键字之后则不会出现上面情况,而是把我们的lambda的相关函数代码逻辑复制过去,相当于C++中的宏定义的代码替换的意思。
list.getOrElse(3){"越界"} // 如果越界了则取后面的内容
list.getOrNull(1) // 越界则取null
set.elementAtOrElse(3){"越界了"}
set.elementAtOrNull(456) ? : "越界啦啦啦啦啦啦"
// mutable 可变成数组和不可变数组是可以相互转化的,KT提供了相关的函数,这里就不说了。
list += "adfa' // 很明显,类似于C++的操作符重载
list[0] // 相当于get,也是操作符重载
list.toSet().toList() //相当于去重
list.distinct() // KT 提供的去重函数,内部转成可变的set再转成list的过程
// 下面的两种创建方式都是等同的
val mMap1 : Map<String, Double> = mapOf<String, Double>("zhangsan" to(564.5), "lisi" to 4564.1)
val mMap2 = mapOf(Pair("zhangsan", 5454.1), Pair("lisi", 6544.1))
// 除了下面两种其它的获取方式类似java的那些常规获取方式存在角标越界风险会崩溃
mMap2.getOrDefault("wangwu", 1.0L)
mMap.getOrElse("wangwu"){1.2L}
// 遍历
mMap.forEach{
// it == 每一个元素 map.Entry
println("K:${it.key} v:${it.value}")
}
mMap.forEach{key,valeu ->
println("$ke, value:$value")
}
mMap.forEach{k,v ->
}
for(item /* :Map.Entry */ in mMap) {
println("key:${item.key} value:${item.value}")
}
// 可变
val map : MutableMap<String, Int> = mutableMapOf(Pair("adf", 123), "dfad" to 456)
map += "aaa" to(111)
map -= "ddd"
map["ssss", 999]
// 如果存在FFF 这个key,则添加到map集合里面,并从map集合中获取,r = 555
val r = map.getOrPut("FFF"){555}
// 上面已经添加了,发现里面有这个key,返回r = 555
var r = map.getOrPut("FFF") {666]
var name = "daye"
/* 背后做的事情:自动生成 set get方法例如下面的set
@NotNull
private String name = "daye";
public void setName(@NotNull String name) {
this.name = name;
}
*/
// 正常kt中隐式藏起来的代码
var value = "abcd"
// 下面的代码写不写都会有,隐藏起来了
get() = field
set(value) {
field = value
}
// field可以看成是变量的this
var info = "abc"
get() = field.capitalize() // 把首字母修改成大写
set(value) {
field = "**{value}**"
}
// kotlin 最让人烦恼的就是一堆非空判断,
var info : String ? = null
fun getShowInfo() : String {
return info?.let{
if(it.isBlank()) {
"info是空值,请检查代码"
} else {
"最终info的结果是:$it"
}
} ? : "info原来是null,请检查代码"
}
// 主构造函数: 规范来说,都是增加_xxx的方式,临时的输入类型,不能直接使用,需要接收成为变量才能用
calss Test(_name: String) // 主构造函数,如果没有参数传递,()可以不写,会隐式帮你完成
{
var name = _name
fun show() {
println(_name) // 临时的输入类型,不能直接用
}
}
class Test1(var name : String, val sex : Int) {
// 参数可以直接使用,相当于
/**
var name = _name
val sex = _sex
*/
}
// 次构造必须调用主构造,否则不通过,主构造统一管理,为了更好的初始化设计
calss Test2(name : String) {
constructor(name: String = "默认值,不传的话就使用", sex: Char): this(name){}
}
// 顺序 1 主构造
class Test3(_name : String) {
// 生成代码 顺序2
var name = _name
// 按照代码执行顺序 3 ,最后才是次构造。 2和3是平等的,只是代码在前面就执行前面的
init{
println("主构造之后会执行当前函数,再执行次构造")
}
}
// 初始化陷阱,不要多此一举帮系统写代码
class Test3(_info : String) {
var content : String = getContent()
var info = _info
private fun getContent() = info
}
fun main(){
Test3("测试").content.length // 直接崩溃了
// 按照正常的逻辑:Test3(“测试”)调用主构造,var info = _info已经执行了
// 之后调用getContent()方法,info已经有值了才对。
// 实际上按照类成员顺序往下调用的执行的
// var info = _info 你不写会自动生成在最上面,但是你自己写了就在那个位置了。
}
data class AddClass(var number1: Int, var number2: Int) {
operator fun plus(p1: Addclass) : Int {
return (number1 + p1.number1) + (number2 + p1.number2)
}
// 查看整个KT可以用的 运算符重载 写出下面代码IDE工具会自动提示
// operator fun AddClass.
}
fun main() {
AddClass(1,1) + AddClass(2,2) // 6
}
// KT想表达,枚举其实也是一个class,为什么?就是为了枚举有更丰富的功能。
enum class Week{
星期一,
星期二,
星期三,
星期四,
星期五,
星期六,
星期日
}
data class LinbsInfo(var limbsInfo: String, var length : Int) {
fun show() {
println("${linbsInfo}的长度是:$length")
}
}
enum class Limbs(private var limbsInfo: LimbsInfo) {
LEFT_HAND(LimbsInfo("左手", 88)),
RIGHT_HAND(LimbsInfo("右手", 88)),
LEFT_FOOT(LimbsInfo("左脚", 120)),
RIGHT_FOOT(LimbsInfo("右脚", 120))
; // 结束枚举值
fun show() = "四肢是:${limbsInfo}的长度是:${limbsInfo.length}"
fun updateData(limbsInfo: LimbsInfo) {
this.limbsInfo.limbsInfo = limbsInfo.limbsInfo
this.limbsInfo.length = limbsInfo.length
}
}
fun main() {
Week.星期一
// 枚举的值等于枚举本身
Week.星期二 is Week // true
// 思路:Limbs.LEFT_HAND 的值等于枚举本身,调用show方法
Limbs.LEFT_HAND.show()
// 更新枚举值
Limbs.RIGHT_HAND.updateData(LimbsInfo("右手2",99))
}
// lambda 使用 inline 返回值不确定是R还是null ,使用takeIf,返回值就是R?
// 模仿rxjava的map,返回值允许null,T.takeIf{aaa} aaa如果是true则返回T本身,如果是false则返回null
inline fun <T,R> map(inputValue: T, mapActionLambda : (T) -> R,
isMap: Boolean = true) = mapAction(inputType).takeIf{isMap}
fun main() {
// 输入类型是Int,返回是String?
val r = map(123){
it.toString() // lambda最后一行是 返回值
}
// 了解String 和String? 不是一回事
r is String // false
r is String? // true
r ?: "返回值是null"
}
T : Person 跟java很接近,只能是Person或者Persong的子类
class Test<T> (vararg objects: T) {
// vararg objects: T 你可以认为是: objects : List
// out 我们的T只能读取,不能修改 , 反之in 亦然,跟java的约束一样,这里直接记成果,推导过程逻辑很容易混淆。
val objectArray : Array<out T> = objects
}
}
// 定义三个Obj类
data class ObjectClass1(val name: String, val age: Int, val study: String)
data class ObjectClass2(val name: String, val age: Int, val study: String)
data class ObjectClass3(val name: String, val age: Int, val study: String)
class Test1() {
inline fun<reified T> randomOrDefault(defaultLambdAction:() -> T) : T? {
val objList: List<Any> = listOf(ObjectClass1("obj1 李四", 22, "学习C")),
ObjectClass2("obj2 王五", 23, "学习C++"),
ObjectClass3("obj3 赵六", 24, "学习c#")
val randomObj: Any? = objList.shuffled().first()
// 随机产生的对象判断: it is T,这个T是泛型传递进来的,如果没有reified关键字是无法判断的
// 如果 it is T == false 返回null as T? 必须加问题 如果是null 则进入后面的lambda
return radomObj.takeIf{it is T} as T?
?: defautLambdaAction()
}
fun main() {
Test1().randomOrDefault<ObjectClass1>{
ObjectClass1("obj1 赵日天", 666, "学习kotlin")}
}
fun main() {
// 原始是这样存储的
mapOf("零".to(0))
// KT 给我们提供的中缀表达式之后
mapOf("一" to 1)
mapOf(Pair("二", 2))
// 系统的中缀表达式
public infix fun<A,B> A.to(that: B):Pari<A, B> = Pair(this, that)
// 模仿 中缀表达式 + 自定义扩展函数 一起用 这是KT学习C++来的
//对第一个参数C1.gogogo 函数扩展 在括号(c2:C2)里面传递一个参数
private infix fun<C1, C2> C1.gogogo(c2 : C2){
println("自定义中缀表达式")
}
123 gogogo “哈哈哈哈”
“456” gogogo 123
}
假如要使用某个扩展函数,它的名字命名特别长,例如:
import com.xxx.xxx.xxx.xxxxxxxxxxxxxxxxxxxxxx as a
直接给它命名为a
原来使用是:list.xxxxxxxxxxxxxxxxx()
命名后;list.a()
fun main(){
val list = listOf("李元霸","李连杰","李小龙")
// 把匿名函数最后一行的返回值 假如一个新的集合,新的集合泛型是R,并且返回新的集合每一个元素都是it == String
list.map{
"姓名是:$it"
}.map{
"$it, 文字的长度是:${it.length}"
}.map{
"[$it]"
}.map{
print("$it ")}
// 输出:[姓名是:李元霸, 文字的长度是:7] ...
// flatMap t == 集合1,集合2,集合3... List>
// filter[true,false] true则加入到新的集合里面,false则不加入 过滤函数
list.filter{
true
}
// 合并操作符zip
val names = listOf("张三","李四","王五")
val ages = listOf(20, 21, 22)
// 把第一个集合 和第二个集合 合并起来,创建新的集合并返回
// 创建新的集合(元素1,元素2,元素3) 元素Pair(K,V) K是第一个集合的元素 v是第二个集合的元素
val zip: List<Pair<String, Int>> = name.zip(ages)
zip.toMap()
zip.toMutableSet()
zip.toMutableList()
// 循环
zip.forEach{
it == 每一个元素Pair(k,v)
}
zip.toMap.forEach{(k,v) ->}
}
// 私有构造函数,不让你创建
class SingletonDemo private constructor() {
// 伴生对象函数里面使用懒加载并使用KT的懒加载安全创建方式,类似java的双重校验,KT帮你实现了
companion object {
val instance: SingletonDeme by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED){SingletonDemo()}
}
fun show(){
println("show")
}
}
fun main () {
SingletonDemo.instance.show()
}
@file:JvmName(“aaa”) 放在类的最顶层,给当前类换一个名字,jvm在编译时会替换
@JvmField 给成员添加该标签,剔除私有代码,之后就可以直接调用了。
@JvmOverLoads KT的方法中有默认参数的,java无法享用,给方法添加该标签之后就可以了,
编译器会帮你重载一个对应的方法给你调用,之后再方法里面再调用原来的方法。
@JvmStatic 给对象的成员添加属性变成静态可以直接调用,在KT的半生类companion中,实际生成的是一个静态内部类,你如果给一个方法添加该标签,实际上外面会生成一个静态方法,但是最终方法里面还是会调用回companion里面,JVM没那么傻生成两个方法。
fun main() {
// create 输入源,没有任何参数给你,输出就行(所有类型、万能类型)
create{
"abc"
1323
true
"aaaaaaaaaaa" // 最后一行返回
}.map{
this
}.map{}.map{}.observer{}
}
// 中转战,保存我们的记录
class RxjavaCoreClassObject<T>(var valueItem : T){// 主构造,接收你传递进来的信息,此信息就是create最后一行的返回
// 操作符最后一行传递到了这里valueItem
}
// 定义 create的lambda里面的返回类型是泛型OUTPUT,也就是会有返回值,但是我暂时不知道是什么,会拿最后一行
//将最后一行的返回值传递到核心类,也就是说返回值就是对象核心类,所有的操作符都返回同一个对象,
// rxjava是内部静态方法函数,KT可以直接给它增加拓展函数,链式调用就出来了。
inline fun <OUTPUT> create(action: () -> OUTPUT): RxjavaCoreClassObject<OUTPUT>
= RxjavaCoreClassObject((action()))
// 增加拓展函数map 变换类型 mapAction: I.() - O ,I.() 是为了可以在lambda里面是用this,
// 对I二次拓展,增加一个匿名函数I.()m返回值自动推导可以省略,这里写出来会更清晰,更容易理解
inline fun<I,O> RxjavaCoreClassObject<I>.map(mapAction: I.() -> o) : RxjavaCoreClassObject<O> =
RxjavaCoreClassObject(mapAction(this))
// 拓展一个observer
inline fun RxjavaCoreClassObject<I>.observer(observerAction : I.() -> unit) = observerAction(this)
委托 == 代理
**委托类 -> 委托的是接口的方法
委托属性 -> 委托的是属性的get set方法(暂时还没使用过,看起来没啥用,就不记了。如果想知道咋回事,写几行代码看一下编译后的代码就知道了,逻辑也很简单,使用方法在属性后面添加同属性委托类型 )
例如:var number : Float by :: floatValue (floatValue是另外一个Float类型的成员)
懒加载委托前面的单例说过了
interface DB{
fun save()
}
class SqlDB() : DB {
override fun save() = println("save to sql")
}
//将接口的实现委托给了参数db,把实现细节委托出去,委托类必须是接口
class CreateDBAction(db: DB) : DB by db
fun main() {
CreateDBAction(SqlDB()).save()
}
class Owner{
/*
var: 自定义委托中,必须有set get
String: 自定义委托中,get必须返回String,set传入String
get/set方法第一个参数,必须包含Owner本类或父类
*/
var text : String by Simple()
}
// 手写的麻烦 class Simple(): ReadWrityProperty{} 之后自动生成代码
// 上面的是使用默认提供的,使用起来更快,只要记住大概规则即可。
class Simple{
private var str:String = "default"
operator fun getValue(owner: Any, pr0perty: KProperty<*>): String{
return str
}
operator fun setValue(owner : Owner, property: KProperty<*>, value: String){
str = value
}
}
// 把viewModel的实现给委托出去,只读类型 这是手写的,实际上系统也为我们提供了,直接可以一键生成
val mainViewModel : MainViewModel By viewModels()
// 为什么是MainActivity的拓展函数,因为创建ViewModel需要当前类对象的this
private fun MainActivity.viewModels() : ReadOnLyProperty<Mainivity, MainViewModel> =
ReadOnlyProperty<MainActivity?, MainViewModel> {thisRef, property ->
ViewModelProvider(this@viewModels).get(MainViewModel::class.java)
}
把不可变数组暴露出去,只能读,内部实现委托给可变数组,能读能改。
val data: List<String> by :: _data
private val _data: MutableList<String> = mutableListOf()
把message委托给TextView
operator fun TextView.provideDelegate(value: Any?,property: KProperty<(>) =
// text 是当前TextView的text,会默认访问getText,setValue同理相反,这是KT的机制
object: ReadWriteProperty<Any?, String> {
override fun getValue.... = text as String?
}
... 省略 setValue ---> text = value
var textView : TextView = findViewById(R.id.xxx)
var message : String? by textView
// 双向绑定,当然这不是DataBinding的双向绑定实现方式,我之前看的DataBinding里面的源码还是
// 通过aapt自动生成对应的代码观察者模式那套实现的
// message 也改变了,因为get方法获取来源是textView的text内容
textView.text = "控件修改了数据"
// textView 的text也改变了,因为message的值的set方法传递到了对应委托的对象里面,将value给到textView
message = "数据发生了改变"
自定义委托中将TextView的委托改为String的委托会出现setValue无法直接设置 this = value,因为String本身没有这样的支持
这个时候就需要使用到KT的反射了,使用当前的property去修改set方法再进行设置。
变量设置String委托必须是成员变量,如果在方法里面是不行的,因为拿到的当前本类对象thisRef是null,使用官方的也不行。
这个有点深入了,感觉暂时没必要研究下去,了解前面的那些就足够了。