Kotlin基础

Kotlin基础

一.Kotlin介绍

1.诞生

在这里插入图片描述

2.Kotlin与JVM

在这里插入图片描述

3.为什么学习kotlin?

在这里插入图片描述

4.Kotlin跨平台特性

在这里插入图片描述

二.变量+常量+类型+条件语句+函数

1.声明变量

在这里插入图片描述

2.内置数据类型

没有基本数据类型,只有引用类型,但是编译器会将部分引用类型编程成基本数据类型,看编译后的字节码文件。
在这里插入图片描述
在这里插入图片描述

3.只读变量

在这里插入图片描述

4.类型推断

在这里插入图片描述

5.编译时常量

在这里插入图片描述

6.条件语句:表达式

在这里插入图片描述

7.String模板

在这里插入图片描述

8.函数

(1)函数头
在这里插入图片描述
(2)函数参数
在这里插入图片描述
(3)Unit函数
在这里插入图片描述

三.匿名函数+函数类型+高阶函数(重点)

1.匿名函数

在这里插入图片描述

2.函数类型与隐式返回

在这里插入图片描述
函数类型格式:(输入参数类型)->返回类型

  • 无输入参数也无返回值:() -> Unit
  • 无输入参数返回值为String类型:()->String
  • 输入参数为String类型,返回值是String类型:(String)->String

3.函数参数

在这里插入图片描述

4.it关键字

在这里插入图片描述

5.匿名函数类型推断(简写代码,只有匿名函数有,具名函数没有)

在这里插入图片描述
在这里插入图片描述

6.lambda表达式(重点面试必问)

在这里插入图片描述

7.高阶函数(重点面试必问)

参数为函数类型或者返回值为函数类型的函数为高阶函数
(1)定义参数为函数类型的函数

fun main(){
    //1。匿名函数
    val printUserChinese = {username:String,age:Int->
        "姓名=$username 年龄=$age"
    }
    val printUserEnglish = {username:String,age:Int->
        "name=$username age=$age"
    }
    //2。将匿名函数作为参数
    println(show("姚甜雪",18,printUserChinese))
    println(show("姚甜雪",18,printUserEnglish))
    //3。简写
    println(show("姚甜雪",19,{ username: String, age: Int ->
        "简写username=$username 简写age=$age"
    }))
    //4。最终简写
    println(show("姚甜雪",19) { username: String, age: Int ->
        "简写username=$username 简写age=$age"
    })
    //5。温故it
    var countL = "helloll".count() {
        it == 'l'
    }
    println(countL)
}
//printUser:参数名称  (String,Int)->String:参数类型是函数 输入参数是String,Int,输出参数是String
fun show(username:String,age:Int,printUser:(String,Int)->String):String{
    return printUser(username,age)
}

(2)定义返回值为函数类型的函数

fun main(){
    //调用高阶函数
    val result =  method("张三")
    println(result)
    val resultStr = result.invoke()
    println(resultStr)
    //调用高阶函数
    val result1 = method1("张三")
    println(result1)
    val result11 = result1.invoke()
    println(result11)
    //调用高阶函数
    val result2 = method2("张三")
    println(result2)
    val result22 = result2.invoke(34)
    println(result22)
    //调用高阶函数
    val result3 = method3("张三")
    println(result3)
    val result33 = result3.invoke(34,"八维")
    println(result33)



}

//参数:name
//返回值:()->String 函数类型
fun method(name:String):()->String{
    return {
        "我是高阶函数的返回的函数数据 $name"
    }
}
//参数:name
//返回值:()->Unit 函数类型 无参数无返回值
fun method1(name: String):()->Unit{
    return{
        println("我是高阶函数的返回的函数数据 $name")
    }
}

//参数:name
//返回值:(age:Int)->String 函数类型 有参数int有返回值String
fun method2(name: String):(age:Int)->String{
    return {
        "我是高阶函数的返回的函数数据 $name 年龄为 $it"
    }
}

//参数:name
//返回值:(age:Int,school:String)->String 函数类型 有2个参数有返回值String
fun method3(name: String):(age:Int,school:String)->String{
    return { age:Int,school:String->
        "我是高阶函数的返回的函数数据 学校$school 年龄为 $age 姓名 $name"
    }
}

8.内联函数(重点面试必问)

在这里插入图片描述

9.lambda和匿名内部类接口回调

在这里插入图片描述

四.null安全与异常

1.null安全

    //1.可空性
    var str:String? = "abc"
    str = null
    //2.安全调用操作符
    str?.toString()
    //3。非空断言操作符
    str!!.toString()
    //4.使用let函数的安全调用(经常使用):不为null时调用里面的
    str?.let {
        if(it.isNotBlank()){//不为""
            it.capitalize()//转大写
        }else{
            "abc"
        }
    }
    //5.空合并操作符:如果为空执行右边,如果不为空执行左边
    str = str ?: "demo"
    println(str)
    //6.let和空合并结合使用
    str =  str?.let {
        it.capitalize()
    } ?:"demo"

2.异常

五.字符串操作+数字类型+标准库函数

1.字符串操作

 /*****字符串******/
    //字符串截取
    val index = NAME.indexOf("o")
    val str = NAME.substring(0 until index )
    println(str)
    //切割
    val data = NAMES.split(",")
    println(data[0])
    //切割解构语法
    val (n1,n2,n3) =  NAMES.split(",")
    println("$n1 $n2 $n3")
    //替换
    val strr = "xiaoming,danni,xiaohong!"
    val str2 = strr.replace(Regex("[xio]")){
        when(it.value){
            "x"-> "1"
            "i"-> "2"
            "o"-> "3"
            else->it.value
        }
    }
    println(str2)
     //==和===:==判断内容 ===判断地址
    val str3 = "Jason"
    val str4 = "Jason"
    println(str3 == str4)//true
    println(str3 === str4)//true
    val str33 = "Jason"
    val str44 = "jason".capitalize()
    println(str3 == str4)//true
    println(str3 === str4)//false
    //遍历
    strr.forEach {
        println("$it")
    }

2.数字操作

在这里插入图片描述

  	/*****数字类型安全转换******/
    //val number:Int = "8.98".toInt()//报错
    // println(number)
    val number1:Int? = "8.98".toIntOrNull()
    println(number1)
    /*****Double转Int数字格式化******/
    println(8.985678.toInt())
    println(8.985678.roundToInt())//四舍五入
    val s:String = "%.2f".format(8.985678)//保留2位小树
    println(s)

3.标准库函数

标准库函数特点:
(1)所有的对象均可调用标准库函数,因为是泛型扩展函数
扩展函数:String封装部分函数,这些函数不够使用,对String扩展函数
扩展函数:fun String.函数名(){},对String扩展函数,只能String对象调用此函数
泛型扩展函数:fun T.函数名(){} 对所有类的扩展函数,所有对象调用此函数
(2)这些函数都在Standard.kt文件中 :Standard标准
标准库函数总结:

  • apply:返回类型是调用者本身+匿名函数中持有的是this==对象本身
  • let:返回类型是匿名函数的最后一行+匿名函数中持有的it==对象本身
  • run:返回类型是匿名函数的最后一行+匿名函数中持有的this==对象本身
  • with(str): with个run基本一样,使用方式不同.返回类型是匿名函数的最后一行+匿名函数中持有的this==对象本身
  • also:返回类型是调用者本身+匿名函数中持有的it==str对象本身
  • takeIf :true执行后面
  • takeUnless :false执行后面

fun main(){
    /******takeIf和takeUnless*****/
    val result = "yaotianxue".capitalize()
    //首字母转大写之前,判断字符串包含yao且长度大于6
    //takeIf满足条件才会执行后面操作
    val result2 = "yaotianxue"
        .takeIf {
            it.length > 13 && it.contains("yao")
        }
        ?.capitalize()//首字母转大写
        ?:"默认值"//如果不满足takeIf条件 默认值
    println(result2)
    /******apply******/
    val file = File("")
    file.setWritable(true)
    file.setReadable(true)
    file.setExecutable(true)
    //针对无法链式编程的代码可以用apply
    val file2 = File("地址").apply {
        setWritable(true)
        setReadable(true)
        setExecutable(true)
    }
    /******let:求一个集合中第一个元素后的平方*******/
    //不用let函数
    val  num1 = listOf(3,2,1).first()
    val result1 = num1 * num1
    println(result1)
    //用let函数:节省变量的声明
    val result22 = listOf(3,2,1).first().let {
        it*it
    }
    println(result22)
    /***also*******/
    val str22 = "yaotianxue"
    val result33 = str22.also {
        println("用户名为:$str22")
    }.also {
        println("用户名转大写 ${str22.capitalize()}")
    }
    println(result33)
    /***run**/
    val str33 = "yaotianxue"
    val result44 = str33.run {
        str33.length
    }
    println(result44)
    //使用run多个函数调用
    val result55 = str33
        .run(::isLong)
        .run(::show)//将isLong方法的返回数据作为参数传递给show方法
    println(result55)
    /**with:是run方法的变体**/
   val  result66 = with(str33,::isLong)
    println(result66)
}

fun isLong(name:String):Boolean{
    return name.length > 10
}

fun show(isLong:Boolean):String{
    return if(isLong) "太长了" else "太短了"
}

fun show2(isLong:Int):String{
    return if(isLong>10) "太长了" else "太短了"
}

六.集合:List+Set+Map

0.数组

在这里插入图片描述

1.List

(1)List的创建和取值
在这里插入图片描述
(2)可变列表
在这里插入图片描述
(3)mutator函数
在这里插入图片描述
(4)集合遍历
在这里插入图片描述
(5)解构
在这里插入图片描述

2.Set

(1)Set集合的创建和取值
在这里插入图片描述
(2)可变集合
在这里插入图片描述

3.Map

(1)Map集合的创建
在这里插入图片描述
(2)读取map
在这里插入图片描述
(3)遍历
在这里插入图片描述
(4)可变集合
在这里插入图片描述

七.对象+接口+抽象类

在这里插入图片描述

1.类和对象

主构造/次构造/默认参数/初始化代码块

//主构造并设置属性
class Student(var name:String = "默认值",var age:Int ){
    //次构造
    constructor(name:String) : this(name,18){
    }
    //初始化代码块
    init {
        println("初始化代码块,每次在实例化对象的时候调用")
    }
}
fun main(){
    //创建对象
    var student = Student("长孙",34)
    println("用户名 ${student.name} 年龄为 ${student.age}")
    //设置属性值
    student.name = "张三"
    student.age = 12
    println("用户名 ${student.name} 年龄为 ${student.age}")
    //使用次构造创建对象
    var student2 = Student("长孙")
    println("用户名 ${student.name} 年龄为 ${student.age}")
    
}

2.继承

类默认是封闭的不能继承,使用open修饰类和方法名,子类可以继承和重写
Animal父类:

open class Animal(val name: String) {
    open fun  show(){
        println("动物的名称 $name")
    }
}

Dog子类:

class Dog:Animal("dog") {
    override fun show() {
        super.show()
        println("小狗子类的方法")
    }
}

Cat子类:

class Cat:Animal("cat") {
    override fun show() {
        super.show()
        println("小猫子类的方法")
    }
}

测试类

fun main(){
    val dog = Dog()
    val cat = Cat()
    //is类型检测
    println(dog is Dog)
    println(dog is Animal)
    //类型转换
    eat(dog)
    //Any是所有类的超类
}

fun eat(animal: Animal){
    animal.show()
}

也可以将Animal定义成抽象类并提供抽象方法

abstract class Animal(val name: String) {
    open fun  show(){
        println("动物的名称 $name")
    }
    abstract fun test()
}

3.接口:interface

定义接口

interface Eat {
    fun eat()
    fun drink()
}

Cat实现接口并重写方法

class Cat:Animal("cat"),Eat {
    override fun show() {
        super.show()
        println("小猫子类的方法")
    }

    override fun eat() {
        println(name+"吃东西")
    }

    override fun drink() {
       println(name+"喝东西")
    }
}

4.数据类:data修饰

(1)安装插件
在这里插入图片描述
(2)定义数据类
在这里插入图片描述
(3)数据实体类

data class Goods(
    val bannerList: List<String>,
    val category_id: Int,
    val goods_attribute: String,
    val goods_banner: String,
    val goods_code: String,
    val goods_default_icon: String,
    val goods_default_price: Int,
    val goods_desc: String,
    val goods_detail_one: String,
    val goods_detail_two: String,
    val goods_sales_count: Int,
    val goods_stock_count: Int,
    val id: Int
)

5.object关键字

(1)对象声明

将类的声明和定义该类的单例对象结合在一起(即通过object就实现了单例模式)
对象声明中不能包含构造器(包括主构造器和次级构造器)

声明一个单例的类

object ApplicationConfig {
}

反编译出的Java代码

public final class ApplicationConfig {
   public static final ApplicationConfig INSTANCE;

   private ApplicationConfig() {
   }

   static {
      ApplicationConfig var0 = new ApplicationConfig();
      INSTANCE = var0;
   }
}
可以在类中初始化一些数据,定义方法
```java
object ApplicationConfig {
    init {
        println("初始化,只会调用一次,因为ApplicationConfig单例")
    }

    fun doSomeThing(){
        println("做一些事情")
    }
}

测试类

fun main(){
    ApplicationConfig.doSomeThing()
    println(ApplicationConfig)
    println(ApplicationConfig)
}

(2)伴生对象

因为在kotlin中是没有static关键字的,也就意味着没有静态方法和静态成员。那么在kotlin中如果想要表达这种概念,可以使用包级别函数(package-level
funcation)和伴生对象(companion object)

使用单例类的写法会将整个类中的所有方法全部变成类似于静态方法的调用方式,而如果我们只是希望让类中的某一个方法变成静态方法的调用方式该怎么办呢?使用伴生对象

语法:

class ClassName {
    // 伴生对象名可以省略,默认为Companion,只有代码块里面的是静态的
    companion object 伴生对象名 {
        // define field and method
    }
}
class App {
    companion object {
        fun getAppContext() {}
    }
}

// 反编译出的Java代码
public final class App {
   public static final App.Companion Companion = new App.Companion((DefaultConstructorMarker)null);

   public static final class Companion {
      public final void getAppContext() {
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

(3)对象表达式
对象表达式常用来作为匿名内部类的实现

private val callBack = object : CallBack {

    override fun onSuccess() {}

    override fun onFailure() {}
}

// 通过对象表达式实现点击事件回调
btn.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View) {
        TODO("Not yet implemented")
    }
})

有这样一个疑问,在对象声明和伴生对象中,java代码调用总会跟上INSTANCE或者Companion,直观上感觉不太友好,能不能像kotlin代码那样可以直接调用?
答案肯定是可以的。需要使用kotlin自带的注解:@JvmStatic或者@JvmField,这样就可以和kotlin调用保持一致。

@JvmStatic既可以修饰属性,也可以修饰方法;而@JvmField只能修饰属性

对于对象声明:

object UserManager {
    @JvmStatic
    fun saveUser()
}

// 反编译出的Java代码
public final class UserManager {
   public static final UserManager INSTANCE;

   // 和之前差别在于,这里是静态方法,在java代码中可以直接调用
   @JvmStatic
   public static final void saveUser() {
   }

   private UserManager() {
   }

   static {
      UserManager var0 = new UserManager();
      INSTANCE = var0;
   }
}

// java调用
UserManager.saveUser();

对于伴生对象:

class App {
    companion object{
        @JvmStatic
        fun getAppContext() {}
    }
}

// 反编译出的Java代码
public final class App {
   public static final App.Companion Companion = new App.Companion((DefaultConstructorMarker)null);

   // 和之前差别在于,这里多出了一个静态方法,它调用的是Companion内的getAppContext()
   // 这样我们可以不用调用Companion.getAppContext(),而可以直接调用该方法
   @JvmStatic
   public static final void getAppContext() {
      Companion.getAppContext();
   }

   public static final class Companion {
      @JvmStatic
      public final void getAppContext() {
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

// java调用
App.getAppContext();

6.注解

定义注解

@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
annotation class Table(val tableName:String)

测试

@Table(tableName = "user")
public class User {
}

@Target元注解说明:
在这里插入图片描述
@Retention元注解说明:
在这里插入图片描述

7.枚举类

定义枚举类

enum class Type {
    DOWN,
    UP,
    MOVE
}

枚举类是闭集,使用when 表达式的时候不需要写else,枚举类就是一种ADT

fun check(type:Type):String{
    return when(type){
        Type.DOWN ->"下"
        Type.UP ->"上"
        Type.MOVE ->"移动"
        //不需要写else
    }
}

8.密封类

(1)密封类是Kotlin中的一个高级类,有如下特点:

密封类是为继承设计的,是一个抽象类;
密封类的子类是确定的,除了已经定义好的子类外,不能再有其他子类。

(2)实现
要声明一个密封类,需要在类名前面添加sealed修饰符。这三个类需要在同一个文件中

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()

使用密封类完成when表达式,是一种高级的ADT

fun eval(expr: Expr): Double = when(expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    // 不再需要 `else` 子句,因为我们已经覆盖了所有的情况
}

(3)枚举和密封类之间的区别

他们的区别是,枚举只能有一个实例,而密封类的子类可以有多个实例,并且密封类的子类可以携带自己独有的状态参数以及行为方法来记录更多的实现信息以完成更多的功能,这是枚举类所不具备的。

八.延迟初始化和惰性初始化(常用)

1.lateinit延迟初始化(activity变量声明的时候用到)

如何在activity中声明一个成员变量?
在这里插入图片描述
Kotlin 通常要求我们在定义成员变量后立即对其进行初始化,因为kotlin中会尽量避免空指针异常,不像java中成员变量都有默认值。以下有2种解决方案
方案1:
在这里插入图片描述
方案2:
如果您声明一个类属性而不初始化它,IntelliJ IDEA 编辑器会警告您,并建议添加一个lateinit关键字。注意8大原始数据类型不能延迟初始化
在这里插入图片描述
Kotlin 通常要求我们在定义成员变量后立即对其进行初始化。当我们不知道理想的初始值时,这样做似乎很奇怪,尤其是在生命周期驱动的 Android 属性的情况下,比如SP操作:

class MainActivity : AppCompatActivity() {
    var sharedPreferences: SharedPreferences = getSharedPreferences("test", MODE_PRIVATE)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

运行报错:为什么会报空指针
在这里插入图片描述
getSharedPreferences方法是activity的方法,当activity没有实例化成功后就调用此方法则会报空指针异常,怎么解决呢?在oncrete方法中获得SP对象,延迟初始化
在这里插入图片描述

2.lazy惰性初始化(activity中获得viewmodel的时候用到,后面结合委托继续讲解)

lazy 惰性初始化的属性初始化操作是提前定义好的 , 在 调用之前自动进行初始化操作 , 如果不调用 , 则不进行初始化 ;
lateinit 延迟初始化的属性初始化操作 , 需要手动进行初始化 , 如果忘了初始化直接调用就会报错 ;

class Hello{
    val name by lazy {initName()}

    fun initName(): String {
        println("初始化 name 属性")
        return "Tom"
    }
}
fun main() {
    // 实例化对象时, name 属性不需要初始化
    var hello = Hello()
    println("实例对象初始化完毕")

	Thread.sleep(1000)

    // 在调用 name 属性后, 才初始化 name 属性
    println("name = ${hello.name}")
}

为什么后面讲解??
之前获得viewmodel

adertimingViewModel = ViewModelProvider(this).get(AdertimingViewModel::class.java)

改用委托后

val userViewModel: UserViewModel by viewModels()

九.泛型

  • 泛型,即 “参数化类型”,将类型参数化,可以用在类,接口,函数上。
  • 与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。
  • 优点:
    1.类型安全:通用允许仅保留单一类型的对象。泛型不允许存储其他对象。
    2.不需要类型转换:不需要对对象进行类型转换。
    3.编译时间检查:在编译时检查泛型代码,以便在运行时避免任何问题。

1.泛型类+泛型接口+泛型函数

(1)泛型类

在这里插入图片描述

举例:接口中返回的数据data为泛型T类型的

data class ApiResponse<T>(val code:Int,val message:String,val data:T) {
    
}

(2)泛型接口

interface IFoodEffect<T>{
    fun effect(item:T)
}
//实现接口
class Banana:IFoodEffect<String>{
    override fun effect(item: String) {
        println(item)//item
    }
 
}
//使用
Banana().effect("常食香蕉有益于大脑,预防神经疲劳,还有润肺止咳、防止便秘")

(3)泛型函数

在这里插入图片描述

2.泛型约束

T:Vip,这样写就表示这里只能传入Vip和其子类。这个就类似Java的 <? extends T> 上界通配符

open class Vip(price:Int)
 
class TApple(var price: Int): Vip(price)
class TFood<T:Vip>(item: T) {
    ...
}

3.形变

out(协变):它只能出现在函数的输出位置,只能作为返回类型,即生产者。

in(逆变):它只能出现在函数的输入位置,作为参数,只能作为消费类型,即消费者。

默认(不变):如果泛型类既将泛型类型作为函数参数,又将泛型类型作为函数的输出,那么既不用out也不用in。

(1)不变:泛型类型即作为输出又作为参数。

/**
 * 泛型接口  不变
 */
interface IUnchanged<T> {
    fun originally():T
    fun originally(item:T)
 
}
class BigStore:IUnchanged<String>{
    override fun originally(): String {
        return "实现泛型接口  不变---返回类型"
    }
 
    override fun originally(item: String) {
       println("------------不变---传参 ---=="+item)
    }
}
 
fun main() {
    println("---------------实现泛型接口------- 不变 默认---------------")
    var iUnchanged=BigStore()
    println(iUnchanged.originally())
    println(iUnchanged.originally("参数"))
}


(2)out(协变)

它只能出现在函数的输出位置,只能作为返回类型,即生产者。作用:可以将子类泛型对象可以赋值给父类泛型对象。

//out
interface IReturn<out T>{
    fun effect():T
}
 
open class Fruit()
class AppleHn():Fruit()
 
//生产者
class FruitMarket:IReturn<Fruit>{
    override fun effect(): Fruit {
        println("FruitMarket effect")
        return Fruit()
    }
}
class AppleHnMarket:IReturn<AppleHn>{
    override fun effect(): AppleHn {
        println("AppleHnMarket effect")
        return AppleHn()
    }
}
 
fun main() {
    //out:可以将子类泛型对象(AppleHn)可以赋值给父类泛型对象(Fruit)
    var fm:IProduction<Fruit> = FruitMarket()
    println(fm.effect())
    //am的引用类型是Fruit对象
    //但是AppleHnMarket返回的是AppleHn对象。
    //这里将AppleHn对象赋值给Fruit对象并返回。
    var am:IProduction<Fruit> = AppleHnMarket()
    println(am.effect())
}

(3)in(逆变)

它只能出现在函数的输入位置,只能作为参数,即消费者。作用:可以将父类泛型对象可以赋值给子类泛型对象。

//in
interface IConsumer<in T>{
    fun spend(t:T)
}
 
class Animal:IConsumer<Fruit>{
    override fun spend(t: Fruit) {
        println("Animal spend Fruit")
    }
}
 
class People:IConsumer<AppleHn>{
    override fun spend(t: AppleHn) {
        println("People spend AppleHn")
    }
}
fun main() {
    //in:可以将父类泛型对象(Fruit)可以赋值给子类泛型对象(AppleHn)
    var fca: IConsumer<AppleHn> = Animal()
    fca.spend(AppleHn())
    println(fca)
    var fcp: IConsumer<AppleHn> = People()
    fcp.spend(AppleHn())
    println(fcp)
}


4.vararg:泛型类一次只能放一个,如果需要放入多个实例呢?

class BookMany<T : AndroidMany>(vararg item: T) {
    var data: Array<out T> = item
}
 
open class AndroidMany(name: String)
class KotlinMany(var name: String, var price: Int) : AndroidMany(name) {
    override fun toString(): String {
        return "KotlinS(name='$name', price=$price)"
    }
}
 
class JavaMany(var name: String, var price: Int) : AndroidMany(name) {
    override fun toString(): String {
        return "JavaS(name='$name', price=$price)"
    }
}
 
fun main() {
    var book = BookMany(
        KotlinMany("初学者", 18),
        KotlinMany("进阶者", 28),
        KotlinMany("终结者", 38),
        JavaMany("全面者", 35),
    )
    println(book)//com.scc.kotlin.primary.classkotlin.BookMany@3d24753a
}
 

添加完多个对象,如果直接打印book,那么获取到的是个BookMany对象,那么怎么获取book里面的的对象?
重载运算符函数get函数,通过[]操作符取值。

class BookMany<T : AndroidMany>(vararg item: T) {
    var data: Array<out T> = item
    operator fun get(index:Int) = data[index]
}
 
fun main() {
    var book = BookMany(
        KotlinMany("初学者", 18),
        KotlinMany("进阶者", 28),
        KotlinMany("终结者", 38),
        JavaMany("全面者", 35),
    )
    println(book)//com.scc.kotlin.primary.classkotlin.BookMany@3d24753a
    println(book[0])//KotlinS(name='初学者', price=18)
    println(book[2])//KotlinS(name='终结者', price=38)
}


十.扩展

标准库提供的很多功能都是通过扩展函数和扩展属性实现,包含类扩展的标准库文件都是以类名加s后缀命名的,例如Strings.kt,Maps.kt等

1.扩展函数

声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。 下面代码为 MutableList 添加一个swap 函数:

fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1] // “this”对应该列表
    this[index1] = this[index2]
    this[index2] = tmp
}

这个 this 关键字在扩展函数内部对应到接收者对象(传过来的在点符号前的对象) 现在,我们对任意 MutableList 调用该函数了:

val list = mutableListOf(1, 2, 3)
list.swap(0, 2) // “swap()”内部的“this”会保存“list”的值

当然,这个函数对任何 MutableList 起作用,我们可以泛化它:

fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
    val tmp = this[index1] // “this”对应该列表
    this[index1] = this[index2]
    this[index2] = tmp
}

2.泛型扩展函数

泛型扩展函数在标准库中随处可见,打开查看let also等
下面自定义泛型扩展函数

fun main(){
    val str = "abc"
    str.easyPrint()
    val  num  = 12
    num.easyPrint()
}
//定义泛型扩展函数
fun <T> T.easyPrint():T{
    println(this)
    return this
}

3.扩展属性

除了给类能够扩展函数,还能为类扩展属性,给String扩展添加一个属性,该属性统计字符串中有多少个a

val String.numVowels
    get() = count{it == 'a'}

fun main(){
    val str = "abc"
    println(str.numVowels)
}

4.对类的伴生对象进行扩展

在kotlin中我们可以对类的伴生对象进行扩展,下面是最基本的代码实现

fun main(){
   Jump.Print("这是对Jump的伴生对象的扩展方法")
}

class Jump{
    companion object{}
}
fun Jump.Companion.Print(str:String){
    println(str)
}

十.函数式编程

十一.Kotlin与java互操作

十二.协程(重点)

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值