自己整理的比较全面的kotlin基础
val和var和常量
1.var是可写的变量
2.val是只可读的变量
3.const val定义常量
range表达式
fun rangeTest(num:Int):Unit{
if(num in 1..10){
println("在1-10范围内")
}else if(num in 11..30){
println("在11-30范围内")
}
}
when表达式
fun whenTest(num: Int):String{
return when(num){
1->{
"是1";
}
2->{
"是1";
}
else ->{
"错误"
}
}
}
string模板
fun defaultValue(name:String,age:Int = 50){
println("name${name}----age${age}");
}
函数定义
fun funTest(name:String,age:Int = 30){
}
fun fTest(){
funTest("name",20)
funTest(name = "5555")
}
NoString类型
fun noThingTest(){
TODO("我待会在来写好这个方法,运行会直接抛出异常")
}
反引号中函数特点
1.用反引号可以把函数名包裹起来随意字符调用
fun main(){
`测试函数##$$$%%^`("xxxxxxxxxx");
}
fun `测试函数##$$$%%^`(name:String){
println("我是一个单引号测试函数")
}
2.方法调用,比如有个java类里面的方法是in()或者is(),在Kotlin中调用的话由于in和is在Kotlin中是关键字,所以调用的时候要用反引号包裹起来
YinHan.`is()`调用 反引号包围起来
匿名函数 返回的永远是最后一行的结果
格式:
变量(){
xxxx
}
val len = "我是一个字符串".count();
println("长度${len}")
//匿名函数
val len2 = "我是一个字符串".count {
it=='我'
}
print("len:${len}--len2:${len2}")
函数的类型&隐式返回
val methodAction :()->String;
methodAction = {
"xxxxxxxx"
//匿名函数不用写return 最后一行就是返回
}
println(methodAction())
等同于
fun methodAction():String{
return "xxxxxxxx";
}
带参数
val methodAction :(String)->String={
//输出
str->"xxxxxxxx"
//匿名函数不用写return 最后一行就是返回
}
println(methodAction("xxxx"))
it关键字
//参数为一个的时候就有个默认的it
var m:(String)->String = {
//代表传递进来的参数
it
}
m("xxxxxxxxxxxxxxxx");
匿名函数的类型推断
// 匿名函数的类型推断,可以不用写返回值类型,会自动推断,省略掉()->返回类型
//如果函数的参数为null、也可以省略调
val m2 ={
//参数为null可以省略
v1:Int,v2:String->
"$v1 $v2"
}
println( m2(1,"xxxxxxxxxxx"))
函数作为参数
//函数作为参数
val m3:(String,String,(String,Int)->Unit)->String = {
name,password,onResult->
onResult("失败",200)
"name:$name,password:$password"
}
//调用
// 方法1
m3("admin","1212",{
msg:String,code:Int->
println("code:$code,msg:$msg")
})
// 方法2
m3("admin","1230"){
msg:String,code:Int->
println("code:$code,msg:$msg")
}
等价于
fun m4(name:String,password:String,onResult:(String,Int)->Unit):String{
onResult("失败",200)
return "name:$name,password:$password"
}
内联 inline
//内联
//1.函数如果有lambad作为参数,就需要声明成内联inline
//2.如果不使用内联。在调用端,就会生成多个对象完成lambad的调用,会造成性能损耗
//3.使用了内联相当于c++中的#define宏定义,宏替换,会把代码替换到调用处,没有任何函数开辟,对象开辟的损耗
inline fun m4(name:String,password:String,onResult:(String,Int)->Unit):String{
onResult("失败",200)
return "name:$name,password:$password"
}
函数引用
函数作为参数来传递或变量赋值引用函数,使用::函数名
fun main(){
//1.变量引用函数
val f = ::funTest;
f("函数引用",200);
//2.做为函数参数
funTest2("函数作为参数",f)
funTest2("函数作为参数",::funTest)
}
fun funTest(msg:String,code:Int):Unit{
print("msg:${msg},code:${code}")
}
fun funTest2(msg:String,onResult:(String,Int)->Unit){
println("msh:${msg}")
onResult("函数引用",200);
}
函数值作为返回值
//函数作为返回值
fun funTest3(msg:String):(String,Int)->String{
println("msg:${msg}")
//返回匿名函数
return {
name2:String,code:Int-> "xxxxxxxx"
}
}
var funTest3 = funTest3("xxx")("121", 200)
println("funTest3:${funTest3}")
Kotlin语言的可空性特点
1.kotlin在初始化的时候默认不可以为null的
2.可以使用val name String? 来申明变量是可以为null的
3.如果变量可能为null的情况下。,可以使用变量?.方法来避免空指针
Kotlin中的let使用
let可以操作本身。他内部有个it就是本身,可以用let操作符来对变量本身做一些处理判断
fun letTest(){
var name:String? = null;
//name=‘’
name = "";
var name2 = name?.let {
if(it.isBlank()){
"默认值"
}else{
it
}
}
println("name:${name2}")
}
非空断言的使用 !!
如果你能百分百确定一个变量不可能为null。就可以使用变量!!.属性来调用,不管变量是否为null都会执行
空合并操作符?:为null执行的操作
如果xx?:"为null就会执行"
如 var name:String?="xxx"; name = null;name?:"我是null的"
自定义异常处理
//自定义异常
fun funTest5(){
try {
var name:String?=null;
//自定义异常处理
checkException(name);
val length = name!!.length;
}catch (e:Exception){
println("error:${e}")
}
}
fun checkException(name: String?) {
name?:throw MyException()
}
class MyException :RuntimeException("你的代码太垃圾了")
字符串截取 substring
//字符串截取
fun substringTest(){
val msg = "I like Kotlin!";
var indexOf = msg.indexOf("e")
var substring = msg.substring(0, indexOf)
//until 从0直到indexOf
var substring2 = msg.substring(0 until indexOf)
}
字符串分割 split
//字符串分割
fun splitTest(){
var msg = "A,B,C,D";
//类型自动转换为List了
var split = msg.split(",")
//解构 c++和kotlin都有 只有四个但是用了五个会报错
var (v1,v2,v3,v4,v5) = split;
print("v5:${v5}")
}
字符串的替换replace
//字符串的替换
fun replaceTest(){
var msg = "lalalalwwwwfcfdsfsd";
//可以些正则表达式也可以字符串
var msg2 = msg.replace(Regex("[law]")){
//it.value相当于没有替换
it.value
when(it.value){
"l"->"0"
"a"->"1"
"w"->"2"
else->it.value
}
}
}
字符串的遍历
fun strFor(){
var msg = "lalalalwwwwfcfdsfsd";
msg.forEach {
//默认有个it代表每一个字母
println("$it")
}
}
类型转换
fun strToInt(){
//toInt字符串换int
val num = "666".toInt();
//这个时候会异常。字符串double类型无法转int
val num2 = "666.66".toInt();
//为了避免这种,可以使用toIntOrNull 异常就返回null
val num3 = "666.33".toIntOrNull();
//double转int
var num4 = 6333.22.toInt();
var num5 = 6333.22.roundToInt();//四舍五入
var num6 = "0.2f".format(62.454)//保留两位小数
}
apply的使用
fun applyTest(){
val msg = "xxxxxxxxxxxxxxxxxxx";
//一般的匿名函数都会自带一个it 但是apply不带it,
// 他带的是this。始终代表的是msg这个对象
//所以可以apply链式调用
msg.apply {
println("长度:${this.length}")
}.apply {
println("最后一位:${this.substring(length-1)}")
}
}
let内置函数的使用
fun letTest2(){
var msg = "xasadasdsad";
val length = msg.let {
//it表示的是msg本身
println("msg$it")
it.length;//返回的是msg的长度 int类型
}
println("msg的长度$length")
//非空判断
var m:String?=null;
val m2= m?.let {
println("m不是null")
"为null"
}?:"m是null的";
}
run内置函数的使用
run内含的是this本身。和apply是一致的,run每次返回的结果都是匿名函数的最后一行的结果,和let是一致的。相当于let和apply的合体
fun runTest(){
val str = "我是一个字符串";
var s = str.run {
//内部是使用this,不是it 最后一句是返回
this.substring(0,this.length-1)
}.run { this.substring(0,this.length-1) }
.run { this.substring(0,this.length-1) }
.run { this.substring(0,this.length-1) }
.run { this.substring(0,this.length-1) }
println("$s") //打印 我是
}
with内置函数的使用
with作用是和run一样的。内含的是this本身,最后一句返回的是结果,但是使用方式不一样,with是一个方法,把变量传递到with里面去:
如 whith(str,::函数) 或者with(str){//处理操作}
fun withTest(){
var str = "我们来演示使用with的使用方式";
//具名函数
var s:String = with(str,::getStr)
//匿名函数
var length = with(s){
//this
length
}
with(length,::println)
}
also内置函数的使用
also的返回类型永远是他本身,和apply是一样的
also的匿名函数里面持有的是it,和let是一样的
fun alsoTest(){
var str = "我们来使用also内置函数";
str.also {
//本身持有的是it
//不管怎么操作,返回值都是str本身
print("${it.length}")
}
}
takeIf内置函数的使用
takeIf主要是判断结果是false还是true,false的hauler就返回null。true的话就返回本身,匿名函数中含it,一般配合空合并?:使用
fun takeifTest(){
//takeIf 主要是判断true和false 如果为false 则返回为null
val n = 10;
var r = n.takeIf {
//结果是false返回null 为true的话返回n本身
n>6
}
println("$r")
//一般takeIf配合空合并使用
var result = n.takeIf {
n>20
}?:"n是小于20的"
println("$result")
}
takeUnless内置函数使用
takeUnless和takeIf是相反的,takeUnless为true返回null,一般配合isNullOrBlank()使用
fun takeUnlessTest(){
//takeUnless和takeIf是相反的
//takeUnless为true返回null,为false返回本身
//takeUnless一般配合isNullOrBlank使用
var str:String? = null;
val r = str.takeUnless {
//it
it.isNullOrBlank()
}?:"str是null的"
println("$r")
}
数组的使用 不可变
1.使用listOf来创建数组
2.使用list.[下标](不推荐使用,容易下标溢出)获取元素
3.使用getOrElse(下标){}来获取元素
4.使用getOrNull(下标)来获取元素
5.使用getOrNull+空合并来配合使用
fun listTest(){
//使用listOf创建集合
var list:List = listOf("A","B","C");
//普通方式获取值 使用下标
var s = list[0]
//使用下标获取很容易导致下标溢出导致崩溃
var s2 = list[9999]
//可以使用getOrElse(下标){匿名函数}
var s3 = list.getOrElse(999){
//下标溢出返回这个
"下标溢出了哦"
}
//还可以使用getOrNull 下标不存在则返回null
var s4 = list.getOrNull(9999)
//一般getOrNull加上空合并一起使用
var s5 = list.getOrNull(9999)?:"下标溢出了哦"
}
可变集合mutableListOf的使用
1.使用mutableListOf来创建
2.可以添加删除元素
3.使用toList()转换成不可变集合
4.使用toMutableList可以转换成可变集合
//可变集合
fun list2Test(){
//可变集合使用mutableListOf
var list:MutableList = mutableListOf("A","B");
//添加
list.add("C")
list.add(2,"D")
//删除 根据下标
list.removeAt(1)
//删除元素
list.remove("C");
println("$list")
//可变转换成不可变
var list2 = list.toList();
//不可变转换成可变
var list3 = list2.toMutableList();
}
可变集合的mutator特性操作
1.可以使用+来添加元素
2.可以使用-来移除元素
fun mutatorTest(){
val list:MutableList = mutableListOf("A","B","C")
//可以使用+添加元素
list += "C"
list += "D"
//可以使用-移除元素
list -= "A"
//移除元素可以使用removeIf来过滤,为true就会移除
list.removeIf{
//移除掉包含A的元素
it.contains("A")
}
println("$list");
}
集合的遍历
1.使用for(item in list)
2.使用forEach
3.使用forEachIndexed 带索引
//集合的遍历
fun list3(){
var list:List = listOf("A","B","C","D")
//第一种遍历 in
for(item in list){
print("元素$item")
}
println()
//第二种遍历 forEach
list.forEach{
print("元素:$it")
}
//第三种 带下标
list.forEachIndexed(){
index,item-> print("索引:$index,元素:$item")
}
}
结构语法
//解构语法
fun list4(){
val list:List = listOf("A","B","C")
//可以分别复制给v1 v2 v3
val (v1,v2,v3) = list;
println("$v1")
//可以使用_来忽略不接受值
val(_,_,v6) = list;
print("$v6")
}
Set集合使用
1.set集合是不重复的
2.使用elementAt(下标)获取元素,容易下标溢出
3.使用elementAtOrElse来获取元素
4.使用elementAtOrNull来获取元素
5.使用elementAtOrNull+空合并
fun setTest(){
//set不重复的集合
val set:Set = setOf("AAA","BBB","CCC");
//获取元素
set.elementAt(0);//这样很容易下标溢出
set.elementAt(999);//下标溢出 程序崩溃
//使用elementAtOrElse
set.elementAtOrElse(0){"下标溢出啦"}
set.elementAtOrElse(9999){"下标溢出啦"}
//使用elementAtOrNull
set.elementAtOrNull(0);//下标溢出就返回null
//elementAtOrNull配合空合并使用
set.elementAtOrNull(9990)?:"下标溢出"
}
可变Set
1.使用mutableSetOf来创建可变set
fun setTest2(){
var set:MutableSet = mutableSetOf("A","B");
set.add("C")
set.remove("A")
set.forEach {
print("元素:$it")
}
}
set和list的转换
1.可以使用toList把set转换list
2.可以使用toSet把list转换set
3.list可以使用distinct去重
//set和list转换
fun setToList(){
var list:List = listOf("A","B","C")
//list转set 去重
var set:Set = list.toSet()
//set在转list
var list2 = set.toList()
//可以使用distinct去重
list2.distinct()
}
array数组使用
//数组
fun arrayTest(){
var intArr = intArrayOf(1,2,3,4,5)
var doubleArr = doubleArrayOf(1.2,3.4,5.6)
var longArr = longArrayOf(2.33.toLong(),6.33.toLong(),5.444.toLong())
var shortArray = shortArrayOf(1,2,3,6)
var charArr = charArrayOf('a','b','c')
var floatArr = floatArrayOf(1.2f,5.6f)
var booleanArr = booleanArrayOf(false,false,true)
//可以存放对象
var objectArr = arrayOf("",2,6.55.toLong())
//array转list
var toList = intArr.toList()
//list转array
var toIntArray = toList.toIntArray()
//下标取值
intArr[0]//容易下标溢出
//使用elementAtOrElse 避免下标溢出
intArr.elementAtOrElse(0){1}
//使用elementAtOrNull来获取 下标溢出就为null
intArr.elementAtOrNull(0)
}
map的创建
1.key to value
2.使用Pair(key,value)
//map
fun mapTest(){
//map的创建
var map:Map = mapOf("k1" to 11,"k2" to 22);
//map的创建
var map2:Map = mapOf(Pair("K1",22),Pair("K2",33))
}
map获取值
//map的值获取 使用[] 没有找到返回null
var k1 = map["k1"]
//使用get来获取 和[]是一样的
var k2 = map.get("k1");
//getOrDefault没有找到返回默认值
map.getOrDefault("k1",-1)
//使用getOrElse 没有找到返回-1
map.getOrElse("k1"){-1}
//使用getValue 尽量避免 会导致崩溃
map.getValue("k1")
map的遍历
//map的遍历
fun mapTest2(){
var map:Map = mapOf(Pair("key1",22), Pair("key2",33));
//遍历1
map.forEach{
//内置it
print("${it.key}---${it.value}")
}
//遍历2
map.forEach {
key: String, value:Int ->
print("${key}---${value}")
}
//遍历3
map.forEach{(k,v)-> print("${k}---${v}")}
//遍历4
for(item in map){
println("${item.key}---${item.value}")
}
}
可变map
1.可以使用mutableMapOf创建可变map
2.可以使用+=添加元素
3.可以使用getOrPut获取元素,不存在则添加
//可变集合
fun map3(){
val map:MutableMap = mutableMapOf();
map += "key1" to 12
map += Pair("key2",22)
map.put("12",55)
//2.获取的时候可以使用getOrPut 如果key不存在则会先把22放进map然后在get返回
map.getOrPut("key3"){22}
}
类的创建和get set
1.类的创建
2.自带的get和set
3.field表示的就是当前字段的本身
//类的定义
class User{
var name = "aaaa"
//默认有一个get()和set(value)方法
get()=field//默认返回 field表示的就是name本身
set(value){
field = value
}
}
fun main(){
var u = User();//创建对象
u.name = "bbbbb";//获取对象的属性
print("${u.name}")
}
计算属性和防范竞态条件
1.val申明的变量是只读的没有set方法
2.当对一个变量进行调用的时候这个变量可能为null,就必须采用防范竞态条件。是KT的规范
fun getUserInfo(str:String?):String{
//当你调用这个成员的时候。可能为null,就必须采用防范竞态条件 这个是KT的规范
return str?.let {
if(it.isBlank()){
"值是null的"
}else{
str
}
}?:"这个值是null的"
}
类的主构造函数(使用_xxx临时输入)
1.一般是用_xxxx作为临时输入类型,必须要成为变量后才可以直接使用
2.val 的变量没有set方法
3.set可以私有化 get不可以私有化
//类的构造函数
// _xxx都是临时输入类型。不可以直接调用 必须要成为变量才可以使用
class Dog(_name:String,_age:Int){
//成为变量才可以使用
var name = _name
//get不可以私有化
private get()=field
val age = _age
//私有化
private get()=field
//val 是没有set方法的
// set(value){
// field = value
// }
fun info(){
//直接调用是不行的
//println("$_name")
println("$name")
}
}
类的主构造函数(直接声明使用)
1.使用 val 变量名:类型 可以直接使用
//类的构造函数
//var name:String 其实内部相当于做了val name = _name
class Cat(var name:String,val age:Int){
fun eat(){
println("name:$name,age:$age")
}
}
类的次构造函数
1.使用constructor关键字
2.次构造函数必须要实现主构造函数,因为主构造函数是统一管理,为了更好地设计
3.当调用次构造函数时候。先会调用主构造函数,然后才会执行次构造函数的代码
//类的次构造函数
class Fish(name:String){
//次构造函数必须要调用主构造函数
//因为主构造函数是统一管理,为了更好地设计
//当调用次构造函数时候。先会调用主构造函数,然后才会执行次构造函数的代码
constructor(name:String,age:Int):this(name){
println("name:$name,age:$age")
}
}
类构造函数的参数默认值
1.构造函数中的参数可以给默认值
2.有默认值的情况下,优先调用主构造函数
//构造函数中的参数默认值
class Tiger(name:String="大老虎"){
//参数给默认值
constructor(name:String="小老虎",age:Int=20):this(name){
println("name:$name,age:$age")
}
}
fun main(){
//次构造函数
Tiger("小老虎",230);
//参数有默认值的情况下优先调用的是主构造函数
Tiger();
}
代码块 init
1.初始化代码块 关键字init{},作用不是java中的static {} 而是java中的{}代码块
2.在初始化的时候执行。一般做一些参数校验 初始化操作
3.require(false){lambda代码} 前面第一个参数为false就会执行带二个参数的lambda代码,一般做参数校验
//代码块 init
class Snake(name:String){
init {
println("初始化了,构造函数执行")
//初始化代码块可以做一些参数校验
//require 第一个参数为false 就会执行后面的lambda代码
require(!name.isNullOrBlank()){
throw RuntimeException("name不可以为null")
}
}
constructor(name:String,age:Int):this(name){
println("次构造函数执行")
}
}
构造初始化顺序
1.先执行主构造函数
2.init代码块和变量初始化同时执行,谁在前面谁就先执行。
3.在执行次构造函数
延迟加载 lateinit
1.用到的地方在加载,需要手动调用初始化加载
2.可以使用::变量名.isInitialized来判断是否加载,避免崩溃
//延迟加载
class T1{
//使用关键字lateinit 懒加载,用到才会去加载
lateinit var name: String;
fun loadRequest(){
///网络操作
name = "xxxxxx";
}
fun getName(){
//会直接崩溃 因为还没有对name进行赋值
println("name:$name")
//可以判断 ::属性名.isInitialized 来判断是否初始化了加载
if(::name.isInitialized){
println("已经初始化加载")
}else{
println("还没初始化加载呢")
}
}
}
懒加载 by lazy
1.lateinit是在使用的时候。手动初始化加载,在使用
2.by lazy是自动的懒加载,在需要的时候自动初始化加载使用
fun main(){
//初始化的时候不会调用p方法
val demo by lazy {
p()
}
println("...........");
//这里才会调用dmeo初始化调用p方法
print(demo)
}
fun p(){
print("使用的时候才初始化")
}
类
1.类的集成必须要用open修饰,转换中java默认是public final
fun main(){
//初始化的时候不会调用p方法
val demo by lazy {
p()
}
println("...........");
//这里才会调用dmeo初始化调用p方法
print(demo)
}
fun p(){
print("使用的时候才初始化")
}
类型转换和类型判断
1.用is判断类型 如 s is Student
2.用as转换 如 s as Student
var s:Student2 = Student2();
if(s is Person2){
(s as Person2).eat()
}
Any超类
1.类是java的Object
2.所有的类都默认继承Any
3.默认实现了toString equals hashCode等
object 单例类
1.用object修饰的类为单例
2.没有主构造函数,私有化
3.object修饰的类既是类名也是单例的实现
4.init类相当于 static{}
//单例类 使用object修饰
//没有构造函数 主构造函数私有化被禁用
object Day87{
var name:String = "单例模式name";
fun printName(){
println("name:$name")
}
//等价于 static {xxxx}
//这里会创建单例的实现 INSTANCE = Day787()
init{
println("初始化....")
}
}
fun main(){
//使用object修饰的既是类名也是类的单例实现
//内部实现等于内部创建了一个 final static Day87 INSTANCE
println(Day87)
//等价于Day87.INSTANCE.name
println(Day87.name)
}
对象表达式
1.kt匿名对象的实现 objetc:对象名(){...}
2.kt具名对象的实现 class XX :YYY(){....}
3.kt实现kt的接口是有一种方式,object:接口名{...}
4.kt实现java的接口有两种方式。1.object:接口名{} 2.接口名{xxxx}
//对象表达式
open class Day88{
open fun f1(){
println("方法1")
}
open fun f2(){
println("方法2")
}
}
open interface RunnableImpl{
fun run()
}
class Day88Impl : Day88() {
override fun f1() {
super.f1()
println("重写方法1")
}
override fun f2() {
super.f2()
println("重写方法2")
}
}
fun main(){
//KT调用对象 匿名对象调用
var d:Day88 = object : Day88() {
override fun f1() {
super.f1()
println("重写方法1")
}
override fun f2() {
println("重写方法2")
}
}
d.f1()
//KT调用对象 具名调用
var d2 = Day88Impl();
d2.f1()
//KT调用接口只有一种方式 object:对象名(){...}
var runnable:RunnableImpl = object:RunnableImpl{
override fun run() {
println("实现接口")
}
}
runnable.run();
//KT调用java的接口有两种方法 1.object:对象名(){...} 2.对象名{...}
var r2:Runnable = object : Runnable{
override fun run() {
println("KT调用java的接口实现方式1")
}
}
//2.接口名{....}
var r3:Runnable = Runnable{
println("KT调用java的接口实现方式2")
}
}
伴生对象 companion
1..KT中没有static静态,所有使用companion来代替static的功能
2.无论对象创建多少次,伴生对象都只会初始化一次,只会加载一次
//伴生对象
class Day89 {
//类的伴生对象 使用关键词 companion,
// kt中是没有static的 这个相当于 static的功能
//内部的实现其实相 当于在Day89类中创建了一个
// final static class Companion{
// private final static String info = "伴生对象info";
// }
companion object{
var info:String = "伴生对象info";
}
}
fun main(){
//1.KT中没有static静态,所有使用companion来代替static的功能
//2.无论对象创建多少次,伴生对象都只会初始化一次,只会加载一次
//3.伴生对象只会初始化一次
println(Day89.info)
}
内部类和嵌套类
1.内部类必须要用inner修饰符修饰
2.内部类可以直接访问外部类的成员变量
3.嵌套类不可以直接访问外部类
4.内部类调用方式 外部类().内部类().xxx
5.嵌套类调用方式 外部类.嵌套类().xxx
class Body{
var name:String? = null;
//嵌套类
//不可以直接调用外部类的成员
class Header{
var header:String = "头部";
fun printHeader(){
println("头部信息:$header")
}
}
//内部类
//可以直接调用外部类成员信息
inner class Footer{
var footer:String="底部";
fun printFooter(){
println("${name}-->底部信息$footer")
}
}
}
fun main(){
//内部类调用
Body().Footer().printFooter();
//嵌套类调用
Body.Header().printHeader();
}
data 数据类型
1.使用data修饰符修饰数据列。类是java中的havaBean,
2.普通列只是有set和get和构造方法。data类默认除了get和set,构造外、还从写了toString equals copy hashCode等方法
3.数据类不能使用abstract open selead inner等关键字
data class Result(var code:Int,var msg:String):Any()
copy函数
1.对象拷贝
2.内置的方法只会考虑主构造函数,不会执行次构造函数的代码,如copy不会执行次构造函数里面的代码。
data class Day92(var name:String,var age:Int){
init {
println("主构造函数执行")
}
constructor(name: String):this(name,20){
println("次构造函数")
}
}
fun main(){
var d = Day92("XiaoA")
var d2 = d.copy()
println("d:$d")
d2.age=30
println("d2:$d2")
println(d==d2)
}
解构申明
1.使用operator关键字申明解构,
2.必须使用component1开始申明
3.顺序必须和成员变量一一对应
class Day93(var name:String,var age:Int,var sex:Char) {
//注意顺序 必须是component1 从1一次顺序开始 和成员顺序一一对应
operator fun component1() = name;
operator fun component2() = age;
operator fun component3() = sex;
}
fun main(){
//解构 可以一一对应对象的属性
var (name,age,sex) = Day93("A",20,'a');
var (_,age2,_) = Day93("B",20,'a');
}
重载运算法
1.可以使用operator fun 运算符(参数):返回类型{
return xxx;
}
来重载运算符
//运算符重载
data class Day94(var num:Int){
//plus代表+运算
//可以使用operator fun Day94. 查看KT支持的所有重载运算符
operator fun plus(num: Int):Day94{
this.num +=num;
return this;
}
}
fun main(){
println(Day94(20)+52) //Day94(num=72)
}
枚举
1.使用关键字enum calss 名{
枚举值,
}
2.枚举值等于本身
//枚举
enum class Day95{
星期一,
星期二,
星期三
}
fun main(){
//枚举的值等于本身
println(Day95.星期一)
//枚举等于本身
println(Day95.星期一 is Day95) //true
}
枚举的一般用法
1.枚举的参数必须和主构造函数参数一致
//身体信息
data class BodyInfo(var header:String,var height:Int){
}
enum class Day96(var body:BodyInfo){
//枚举的主构造函数必须和枚举参数保持一致
Man(BodyInfo("男人",20)),
Woman(BodyInfo("女人",18));
fun show(){
println("头部:${this.body.header},高度:${this.body.height}")
}
fun update(body: BodyInfo){
this.body = body;
println("更新后${this.body}")
}
}
fun main(){
Day96.Man.show()
Day96.Woman.show()
Day96.Man.update(BodyInfo("xxx",50))
Day96.Woman.update(BodyInfo("xxx",60))
}
密封类
1.密封类其实是枚举的扩展,可以展示不同的实例,
2.主要配合when来使用,可以容纳所有的条件
3.使用关键字sealed
//密封类
sealed class Ui{
object show:Ui()
object hide:Ui()
class TranslateX(val x:Int):Ui()
class TranslateY(val x:Int):Ui()
}
fun main(){
var op:Ui = Ui.TranslateX(20);
when(op){
Ui.show-> println("显示组件")
Ui.hide-> println("隐藏组件")
is Ui.TranslateX-> println("偏移量x-->${op.x}")
is Ui.TranslateY-> println("偏移量y-->${op.x}")
}
}
接口的定义
1.使用关键字interface申明
2.接口不能有构造函数
3.实现接口必须要实现接口的成员以及方法
4.接口的成员和方法都默认加了open
//1.接口定义
//接口内的所有成员都默认加了open
//接口不能有主构造函数
//实现接口不仅要实现接口方法,还要实现接口成员
interface Usb{
var name:String //usb名字
fun insertUSB():Unit //usb插入
}
class Mouse(override var name: String="鼠标"):Usb{
override fun insertUSB(): Unit {
println("${name}插入了USB")
}
}
class KeyBoard() :Usb{
override var name: String = "键盘"
set(value) {
println("设置数据")
field = value;
}
get() {
println("获取数据")
return field;
}
override fun insertUSB() {
println("${name}插入了USB")
}
}
fun main(){
var m:Mouse = Mouse();
m.insertUSB()
var k:KeyBoard = KeyBoard()
k.name = "键盘";
k.insertUSB()
}
接口的默认实现
1.可以给接口的成员重写get方法来给成员默认值
2.接口的规范来说这样是不可以的
3.接口的val只读后面也是可以修改的
interface Usb{
//必须得val
val name:String //usb名字
get() {return "一个默认值"}
fun insertUSB():Unit //usb插入
}
抽象类
1.使用关键字abstract申明
2.抽象类可以有实现也可以有抽象方法
3.继承抽象类必须要实现所有的抽象方法
abstract class baseActivity{
fun onCreate(){
getLayoutId()
initView();
initData();
initXXX();
}
abstract fun getLayoutId():Int
abstract fun initView()
abstract fun initData()
abstract fun initXXX()
}
class LoginActivity :baseActivity(){
override fun getLayoutId(): Int {
println("获取布局ID")
return 20;
}
override fun initView() {
println("初始化View")
}
override fun initData() {
println("初始化Data")
}
override fun initXXX() {
println("初始化xxx")
}
fun show(){
super.onCreate()
}
}
fun main(){
LoginActivity().show()
}
泛型类的使用
1.和java的泛型类很相似
//使用泛型类
class Day102(val obj:T){
fun show(){
println("万能输出器:${obj}")
}
}
data class Teacher2(var name:String,var age:Int)
fun main(){
var t:Teacher2 = Teacher2("张三",20)
Day102(t).show()
}
泛型方法
fun show(item:A):A?{
val r = item?.apply {
println(this)
item
}?:item
return r
}
泛型实战之类型转换
1.类是rxjava的map操作符
2.可以将任意类型转换成其他任意类型
class Day103 (var isMap:Boolean,var input:T){
fun map(mapAction:(T)->R):R? = mapAction(input).takeIf { isMap }
//两种是等价的
fun map2(mapAction:(T)->R): R {
return mapAction(input)
}
}
//高级一点写法
inline fun map(input:I,mapAction: (I) -> R):R = mapAction(input)
fun main(){
var d = Day103(true,12212)
var r = d.map {
it.toString()+"5555"
}
println(r)
val r2 = map("95922323"){
it.toInt()
}
println("$r2")
}
泛型类型限制
1.类是java中的T extends XXX类
2.只有限制类本身和它的子类可以使用
3.T:限制类
open class C1{}
open class C2:C1(){}
class C3:C2(){}
class C4(cls:T):C2(){
fun show(){
println("只有C2的子类以及本身可以访问这个")
}
}
fun main(){
val c1:C1 = C1();
val c2:C2 = C2();
val c3:C3 = C3();
//泛型类型限制只有c2以及他的子类才可以调用
var c4 = C4(c1)
var c5 = C4(c2)
var c6 = C4(c3)
}
不定参数 vararg
1.使用关键字申明vararg
//不定参数
class Day105(vararg var tags:Any){
var data = tags;
fun show(){
for (item in data) println("参数:$item")
}
}
fun main(){
var d = Day105("12",12,-1)
d.show()
}
out 协变
1.修饰的泛型只可以获取,不可以修改
2.修饰后的泛型子类可以赋值给泛型的父类
3.相当于java中的? extends T
4.父类=子类
//生产者
//out修饰的泛型只能输出不能修改
interface Produce{
//这个时候就会报错。因为使用out标记的泛型不可以修改
// fun updateConsumer(consumer:C){
//
// }
//可以获取泛型
fun getConsumer():C;
}
class Produce1():Produce{
override fun getConsumer(): Animal {
return Animal()
}
}
class Produce2():Produce{
override fun getConsumer(): Man {
return Man()
}
}
class Produce3():Produce{
override fun getConsumer(): Woman {
return Woman()
}
}
open class Animal{
}
//为Animal的子类
class Man:Animal(){
}
//为Animal的子类
class Woman:Animal(){
}
class Day109 {
//不加out 不会报错
var p1:Produce = Produce1();
//没有out会报错,因为 Produce2接受的泛型是Man但是使用Animal也可以。因为使用了out 相当与java中的 ? extends T
var p2:Produce = Produce2();
//没有out会报错
var p3:Produce = Produce3();
//泛型的默认情况下,泛型的子类对象不可以赋值给泛型的父类对象 比如Produce无法赋值给Produce
//out 关键字可以吧泛型的自子类对象赋值给泛型的父类对象
}
in 逆变
1.泛型中默认泛型父类不可以赋值给泛型子类
如 //这样写是错误的。泛型的父类对象不可以复制给泛型的子类对象
//KT中使用in关键字修饰就可以泛型的父类对象可以赋值给泛型的子类对象
List list2 = new ArrayList();
2.但是KT中使用in关键字,就可以泛型的父类对象赋值给泛型子类对象
3.子类=父类
interface Produce{
fun updateConsumer(consumer:C){
}
//这里就会报错 in只能修改不可以输出
fun getConsumer():C;
}
class Produce1():Produce{
override fun getConsumer(): Animal {
return Animal()
}
}
class Produce2():Produce{
override fun getConsumer(): Man {
return Man()
}
}
class Produce3():Produce{
override fun getConsumer(): Woman {
return Woman()
}
}
open class Animal()
//为Animal的子类
class Man:Animal()
//为Animal的子类
class Woman:Animal();
class Day109 {
//使用关键字in可以泛型的父类可以赋值给泛型的子类
var p1:Produce = Produce1();
var p2:Produce = Produce1();
}
reified关键字
1.reified一般作用在泛型上,如果要使用is等和T做比较,就必须在泛型申明前面加上reified
2.使用reified一定要在方法前面加上inline关键字
3.实例化类型参数
//随机生成三个类。如果生成的类不等于用户规定的泛型类。那么就返回默认值
///申明三个data类
data class D1(var name:String)
data class D2(var name:String)
data class D3(var name:String)
class Day110 {
//使用reified必须加上inline
inline fun randomClass(defaultAction:()->T):T?{
val list = listOf(D1("D1"),D2("D2"),D3("D3"))
var d:Any? = list.shuffled().first()
//这里使用it is T 就必须加上reified关键字
//这里使用后T?是如果it is T为false就返回null null去转换as T 就会报错 所以使用T?
return d.takeIf { it is T } as T? ?:defaultAction()
}
}
fun main(){
val r = Day110().randomClass{
D1("默认D1")
}
println(r)
}
扩展
1.Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
class Day115{
var name:String="Day115"
}
//扩展函数里面可以访问扩展类的所有成员和方法
fun Day115.show(){
println("我是Day115的扩展函数-->${name}")
}
fun main(){
Day115().show()
}
泛型扩展
1.所有的对象和方法都是泛型
2.扩展泛型所有的对象都可以调用
3.扩展函数内部本身包含this
//泛型扩展类
//所有的类都是泛型 都可以使用
fun T.out(){
println("输出本身:$this")
}
fun I.outTime(){
println("调用时间:${Date().time}")
}
fun main(){
//任何对象都可以调用
"A".out();
12.out();
//支持链式调用
"123".out().outTime()
}
泛型扩展的实例
1.let等原理
//let原理
//对泛型扩展 内部持有it 返回最后一行
private inline fun I.mLet(label:(I)->O):O = label(this)
扩展属性
//扩展属性
var String.myInfo:String
get()="xxxxxxxxxxxx"
set(value) {
}
调用println("xxx".myInfo)
可空类型的扩展
//可空类型的判断
fun String?.info():String{
if(this.isNullOrBlank()){
return "默认值";
}
return this;
}
infix 终缀表式
1.可以简化代码
2.必须要结合扩展使用
3.必须传递一个参数
//终缀表示式infix
//可以简化代码
//1.结合扩展使用
//2.必须要给定传递参数
//写法"yyyy".into(555) 可以简化 "xxxxxx" into 55
infix fun C1.into(c:C2):C1{
println("接受的参数${c}")
println("this:$this")
return this
}
fun main(){
val r = "xxxxxx" into 55
"yyyy".into(555)
println("r:$r")
}
扩展文件
1.扩展文件就是把一系列的扩展函数放在一个文件里面,其他地方导入包调用
重命名
1.有时候第三方的类名或者方法名太长了,可以使用as重命名
import com.l024.test.kotlin.Day105 as d105
apply的原理
1.apply始终返回的是本身
2.apply内部持有的是this
3.匿名函数持有this使用T.()->Unit这种方式
//1.apply始终返回的是本身
//2.apply内部持有this
//T.()->Unit是为了匿名函数持有一个this
fun T.mApply(lambda:T.()->Unit):T{
lambda()
return this;
}
fun main(){
"123".mApply {
println("this:$this")
}.mApply {
println("")
}
}
map扩展的使用
1.list.map可以将list中每一个item转换成其他类型。
2.返回是一个新的list
3.把每一个item(String等类型)加入一个新集合
//map的原理
inline fun Iterable.mMap(transform:(I)->O):List{
var list:MutableList = mutableListOf();
for(item in this){
list.add(transform(item))
}
return list
}
//map的使用
fun main(){
var list:List = listOf("a","b","c")
//map可以转换每一项item返回新的list
// list.map{
// //返回每一项
// it.uppercase(Locale.getDefault())
// }.map {
// println("$it")
// }
list.mMap {
it.uppercase(Locale.getDefault())
}.mMap {
println("$it")
}
}
flatMap
1.和map差不多,但是flatMap是把每一个元素it转换成一个集合,返回的时候是一个List> 内部最后会处理成一个List返回
fun main(){
var list:List = listOf("A","B")
//flatmap中是把item转换成一个list,然后返回List> 最后处理成一个list返回
var l = list.flatMap {
//这里返回的是一个list
listOf("$it--1","$it--2")
}
println(l)
//结果 [A--1, A--2, B--1, B--2]
}
filter 过滤
1.一般配合flatMap使用
2.返回true就把元素添加到一个新的list返回
fun main(){
var list:List> = listOf(
listOf("A"),
listOf("B"),
listOf("C"),
)
//使用map来过滤
val l = list.map {
it.filter {
it=="A"
}
}
println("map-->过滤结果:$l")
//map-->过滤结果:[[A], [], []]
var l2 = list.flatMap {
//filter为true就把元素新增到一个list中返回。为false就不添加
it.filter {
it=="A"
}
}
println("flatMap-->过滤结果:$l2")
//flatMap-->过滤结果:[A]
}
zip
1.把两个数组合并成一个数组
2.第一个数组的值为KEY
3.第二哥数组的值为value
4.返回的类型其实是List>
fun main(){
var list1:List = listOf("A","B","C")
var list2:List = listOf(1,2,3)
//使用zip将两个list合并成一个
//返回的是一个List>类型
//zip把第一个数组的值当做K,第二个数组的值当做V
var zip:List> = list1.zip(list2)
println("zip:$zip")
//zip:[(A, 1), (B, 2), (C, 3)]
//遍历
var toList = zip.toList()
println("toList:$toList")
//toList:[(A, 1), (B, 2), (C, 3)]
var toMap = zip.toMap()
println("toMap:$toMap")
//toMap:{A=1, B=2, C=3}
//遍历zip
zip.forEach {
//it 其实是一个Map.Entry类型
println("key:${it.first},value:${it.second}")
}
//普通方法
zip.toMap().forEach(){k,v->{
println("k:${k},v:${v}")
}}
//解构方法
zip.toMap().forEach(){(k,v)->{
println("k:${k},v:${v}")
}}
}
单例模式 java&KT
1.饿汉式 开始就调用初始化
2.懒汉式 需要用的时候才会初始化
java版饿汉式
public class Day130Java {
private static Day130Java instance = null;
private Day130Java(){}
static {
instance = new Day130Java();
}
public static Day130Java getInstance(){
return instance;
}
}
Kt版饿汉式
object 类名{}
java版懒汉式
class Demo2{
private static Demo2 instance = null;
private Demo2(){}
public static Demo2 getInstance(){
if(instance==null){
instance = new Demo2();
}
return instance;
}
}
KT版懒汉式
class KtDemo1{
companion object{
private var instance:KtDemo1? = null
get() {
if(field==null){
field = KtDemo1()
}
return field
}
fun getInstanceAction():KtDemo1{
//两个!作用就是确定instance不为null
return instance!!;
}
}
fun show(){
println("show")
}
}
java版懒汉式 保证安全
//懒汉式 保证安全
class Demo3{
private static Demo3 instance = null;
private Demo3(){}
public static Demo3 getInstance(){
if(instance==null){
synchronized (Demo3.class){
if(instance==null){
instance = new Demo3();
}
}
}
return instance;
}
}
KT版懒汉式 保证安全 使用by lazy
//懒汉式 保证安全
class KtDemo2 private constructor(){
companion object{
private val instance:KtDemo2 by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
KtDemo2()
}
}
}
@flie:JvmName(“类名”)
1.这个注解可以在编译时期修改我们的类名
2.这样java在调用KT类的时候就比较方便
3.这个注解必须写在package的外面
@file:JvmName("xxx")
@JvmField注解
1.这个注解可以在java中直接通过类访问到成员变量
2.没有这个注解java中只能通过get属性名来访问成员变量
KT代码
class Day132 {
@JvmField
val list:List = listOf("A","B")
}
java代码
public class Day132_ {
public static void main(String[] args) {
//没有加@JvmField注解
//调用KT类
Day132 day132 = new Day132();
//这样直接调用变量是不行的
// day132.list
// 只能通过getList获取Kt类中的属性
List list = day132.getList();
//加了注解@JvmField就可以直接调用
List list1 = day132.list;
}
}
@JvmOverloads注解
1.java调用KT方法不支持默认参数形式
2.加了注解就可以使用KT方法的默认值
kt代码
//不加注解java调用就必须要两个参数,java不支持KT的默认参数
@JvmOverloads
fun funText(name:String="xxx",age:Int=20){
}
java的代码
//加了注解就可以使用KT的默认参数
Day132Kt.funText("xxxx");
@JvmStatic注解
1.java中调用KT静态方法都必须还得调用下内部类才可以调用
2.加了注解就可以直接调用
kt代码
class Day133 {
companion object{
fun show(){
println("静态方法")
}
@JvmStatic
fun show2(){
println("静态方法")
}
}
}
java代码
public class Day133_ {
public static void main(String[] args) {
//调用KT的静态方法
//必须点Companion然后才可以调用 因为KT原理中是生成了一个内部类
Day133.Companion.show();
//可以加上注解JvmStatic 就可以直接调用静态方法
Day133.show2();
}
}
实现RxJava基本原理
//rxjava的核心类
class RxjavaCore(var value:T){
}
//RxJava的create
inline fun