kotlin基础知识复习

kotlin基础知识复习

  • range 范围 从哪里 到哪里
  • Double转Int与类型初始化
  • 尽量使用内联函数inline
  • List和set集合防止空指针和数据获取
  • list去重
  • Map
  • field 关键字学习
  • 防范竞太条件
  • 构造函数
  • 运算符重载
  • 枚举
  • 泛型复习
  • 泛型约束
  • 中缀表达式
  • 重命名
  • 了解KT的变换函数
  • KT单例类似java的双重校验效果
  • 记录一些KT的标签
  • 基础学习,模仿Rxjava操作符
  • 委托
    • 自定义委托

range 范围 从哪里 到哪里

之前没用过,记一下。

val number = 148
if(number in 10..59) {
} else if (number in 0..9) {
}

Double转Int与类型初始化

// 四舍五入
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基础类型。

尽量使用内联函数inline

在项目当中我们会大量使用函数作为参数进行传参,就是使用lambda作为参数,需要声明成内联

fun test(sername: String, reapnseResult:(String,Int) -> Unit) {}

类型上面这样的函数,内部会生成多个对象来完成lambda的调用(性能损耗)
而加了inline 关键字之后则不会出现上面情况,而是把我们的lambda的相关函数代码逻辑复制过去,相当于C++中的宏定义的代码替换的意思。

List和set集合防止空指针和数据获取

list.getOrElse(3){"越界"}  // 如果越界了则取后面的内容
list.getOrNull(1) // 越界则取null

set.elementAtOrElse(3){"越界了"} 
set.elementAtOrNull(456) ? : "越界啦啦啦啦啦啦"
// mutable 可变成数组和不可变数组是可以相互转化的,KT提供了相关的函数,这里就不说了。

list += "adfa'   // 很明显,类似于C++的操作符重载
list[0] // 相当于get,也是操作符重载 

list去重

list.toSet().toList() //相当于去重
list.distinct() // KT 提供的去重函数,内部转成可变的set再转成list的过程

Map

// 下面的两种创建方式都是等同的
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]

field 关键字学习

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()

了解KT的变换函数

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) ->} 
}

KT单例类似java的双重校验效果

// 私有构造函数,不让你创建
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()
}

记录一些KT的标签

@file:JvmName(“aaa”) 放在类的最顶层,给当前类换一个名字,jvm在编译时会替换

@JvmField 给成员添加该标签,剔除私有代码,之后就可以直接调用了。

@JvmOverLoads KT的方法中有默认参数的,java无法享用,给方法添加该标签之后就可以了,
编译器会帮你重载一个对应的方法给你调用,之后再方法里面再调用原来的方法。

@JvmStatic 给对象的成员添加属性变成静态可以直接调用,在KT的半生类companion中,实际生成的是一个静态内部类,你如果给一个方法添加该标签,实际上外面会生成一个静态方法,但是最终方法里面还是会调用回companion里面,JVM没那么傻生成两个方法。

基础学习,模仿Rxjava操作符

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,使用官方的也不行。
这个有点深入了,感觉暂时没必要研究下去,了解前面的那些就足够了。

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