美文网首页
Android-Kotlin

Android-Kotlin

作者: BoxJing | 来源:发表于2024-12-19 18:16 被阅读0次
1. 类的定义和解构声明

在kotlin中类在定义的时候可以直接带上构造函数传入参数,内部可以再有其他的次要构造函数,就像下面Student类中的constructor(uid: Int) : this(uid, "Biu"),次要函数必须要调用主要构造函数,也就是类定时候外面的构造函数。在类中还可以设置多个init函数,系统会按照顺序从前往后执行代码,可以将不同的内部代码块放进不同的init里面方便管理。下面示例中还有一个运算符重载函数operator fun plus(student: Student):String,实现后就可以用val str = s+s1这样的加法来直接实现对象的相加拿到一些想要的数据。在Student的类中,最后有operator fun component1() = nickName这样的代码,就是结构声明,然后使用val (c) = s1就等于是拿到了s1.nickNameval (c,d) = s1这样就等于是c=s1.nickName,d=s1.uid。也可以用Lambda表达式中使用val func: (Student) -> Unit = {(a,b) ->

fun main() {
    val s = Student(12)
    val s1 = Student(14,"阿狸")
    println("打印:${s.text}")
    val str = s+s1
    println("拿到了 + :$str")
    val (c) = s1
    println("拿到了:$c")

  val func: (Student) -> Unit = {(a,b) ->
        println("拿到了>>:$a")
        println("拿到了>:$b")
    }
    println(func(s1))

}
class Student constructor(private var uid: Int, var nickName: String) {
    var text: String = ""
    constructor(uid: Int) : this(uid, "Biu") //次要构造函数
    {
        println("调用了次要构造函数")
    }
    //    初始化的额外操作可以放在init方法中
    init {
        text = if (uid > 100) {
            "大于1000"
        } else {
            "小于1000"
        }
    }
    init {
        println("初始化了2")
    }
    init {
        println("初始化了1")
    }
    operator fun plus(student: Student):String {
        return nickName + student.nickName
    }
    operator fun component1() = nickName
    operator fun component2() = uid
}
2. 访问权限控制

private:尽在此类中可见(包括其所有成员)
protected:与private可见性类似,外部无法使用,但在子类中可以使用
internal:本项目中任何地方都能访问,别人引用该项目时不能访问
public:任何地方都可以访问(默认的)

3. 类的扩展

扩展函数可以方便的对类扩展出来自己的一些通用方法从而方便使用。比如上面定义的Student对象,扩展出来一个box()方法:

fun Student.box() = println("哈哈哈")

就可以使用Student实例来来调用box方法:s1.box()
像下面这个情况,在一个B类中,对另一个A类进行扩展,那么这个扩展函数就只能在B类中使用,其他地方无法使用A类的这个扩展:

class A {
}
class B {
    private fun A.box() = println("哈哈哈")
    init {
        val a = A()
        a.box()
    }
}
4. Lambda表达式

Lambda表达式只需要在花括号中编写函数体,默认情况下,如果只有一个参数,可以使用it代表传入的参数,如果有多个参数,则需要指定变量名来使用,最后一行即是返回的值:

val funcA:(String) -> Int = {
      println("拿到的 $it")
      20
}
funcA("XXX")

val funcB:(String,Int) -> Unit = { inputStr: String, i: Int ->
     println(" B 拿到的 $inputStr  > $i")
 }
funcB("B",20)
5. 高阶函数

当一个函数的参数是另一个函数的时候,就构成了高阶函数,可是使用Lamda表达式来方便的传参,只有最后一个参数是函数的时候,才能使用Lamda表达式:

// 在main中调用
fun main() {
  funcC {
    println("函数打印: $it")
    "哈哈哈"
  }
funcD(20) {
     println("函数D打印: $it")
     "溜溜"
   }
}

fun funcC(fund: (String)-> String) {
    println(fund("呵呵呵呵"))
}
//多参数 最后一个参数是函数
fun funcD(a:Int,funF: (String)-> String) {
    println(funF("呵呵呵呵"))
}

最后的输出结果是:

函数打印: 呵呵呵呵
哈哈哈
函数D打印: 呵呵呵呵
溜溜
6. 协变和逆变
//定义类
class Box<T>(var v:T)
val box1:Box<Int> = Box(888)
val box2:Box<Number> = box1

上面的代码会报错,正常的思维会觉得Int是Number的子类,代码中用Box<Number>来接受一个Box<Int>应该没问题,在kotlin的泛型中,默认是抗变:

协变:Int是Number的子类,Box<Int>同样是Box<Number>的子类,可以直接转换
逆变:和上面相反,Box<Number>可以直接转换为Box<Int>,前者是后者的子类
抗变:Box<Int>和Box<Number>没有任何关系,无法相互转换

正因为默认是抗变,所以即使2个类型存在父子关系,在编译器那里也无法通过,为了处理这种情况,Kotlin提供了2个关键字:

out:标记一个类型参数作为协变,可以实现子类到父类的转换
in:标记一个类型参数作为逆变,可以实现父类到子类的转换

上面的转换代码改为val box2:Box<out Number> = box1,实现了子类转换为父类,编译器就正常不报错了。

  • 使用out修饰的泛型不能用作函数的参数,对应类型的成员变量setter也会被限制,只能当做一个生产者使用
  • 使用in修饰的泛型不能用作函数的返回值,对应类型的成员变量getter也会被限制,只能当做一个消费者使用
7. 泛型的界限

前面定义泛型的时候:

class Score<T>(val name:String,val uid:String, val v: T)

意味着,最后一个属性v的类型是不固定的,如果想将T限制在Number以及子类的时候,可以:

class Score<T:Number>(val name:String,val uid:String, val v:T)

这样限制后,Score("x","1233",67)中最后一个变量只能是Number或者子类的类型。
再比如在函数中:

fun <T : Number> add(a: T, b: T): Double {
    return a.toDouble() + b.toDouble()
}

val sum = add(5, 10)  // 允许
// val error = add("Hello", "World") // 编译错误:String 不是 Number 的子类

泛型 T 必须是 Number 或其子类,例如 Int、Double。也可以设置多个上界,需要使用where关键字:

fun <T> copyIfNonNull(source: T, target: MutableList<T>)
    where T : CharSequence, T : Comparable<T> {
    if (source.isNotEmpty()) {
        target.add(source)
    }
}

val targetList = mutableListOf<String>()
copyIfNonNull("Hello", targetList)
println(targetList)  // 输出:[Hello]

// val error = copyIfNonNull(123, targetList) // 编译错误:Int 不满足 CharSequence
8. 序列 Sequence

序列会根据操作流程,对数据操作进行优化,大大提高处理效率,并且只有在使用序列中的数据时才会执行定义好的工序,下面是用list实现一个流程:

val list = listOf("AA","BBB","CC","DDD","EEE","FF","GGG","HH","III")
    val result = list.filter {
        println("判断长度:$it")
        it.length > 2
    }.map {
        println("小写转换:$it")
        it.lowercase()
    }.take(3)
//    println("拿到了:$result")

就算没有最后的拿到了打印信息,也会出现下面的控制台输出,说明工序直接执行了,加上打印信息后,会多一行打印:拿到了:[bbb, ddd, eee]

判断长度:AA
判断长度:BBB
判断长度:CC
判断长度:DDD
判断长度:EEE
判断长度:FF
判断长度:GGG
判断长度:HH
判断长度:III
小写转换:BBB
小写转换:DDD
小写转换:EEE
小写转换:GGG
小写转换:III

采用序列后:

val list = listOf("AA","BBB","CC","DDD","EEE","FF","GGG","HH","III")
    val result = list.asSequence().filter {
        println("判断长度:$it")
        it.length > 2
    }.map {
        println("小写转换:$it")
        it.lowercase()
    }.take(3)

执行上面这段代码是没有任何打印信息的,因为没有用到序列中的数据,加上打印println("拿到了:${result.joinToString()}")后,控制台输出的信息:

判断长度:AA
判断长度:BBB
小写转换:BBB
判断长度:CC
判断长度:DDD
小写转换:DDD
判断长度:EEE
小写转换:EEE
拿到了:bbb, ddd, eee

可以看到执行到EEE之后就没有后面的处理工序了。序列并不是随便使用的,要根据实际情况判断是否需要使用序列,在数据量特别庞大的情况下,使用序列处理会更好,但是如果数据量很小,使用它反而会增加开销。

9. 伴生对象

在 Kotlin 中,伴生对象(companion object)是用于定义与类关联的单例对象的一个特殊概念。它通常用来实现与类相关的静态方法和属性,但比传统的静态成员更灵活和功能更强大。

  1. 属于类,而不是实例:
    • 伴生对象的成员可以通过类名直接访问,无需创建类的实例。
  2. 可实现接口:
    • 伴生对象可以实现接口,从而定义某些行为。
interface Logger {
    fun log(message: String)
}

class NetworkService {
    companion object : Logger {
        override fun log(message: String) {
            println("Log: $message")
        }
    }
}

NetworkService.log("Network connected") // 输出: Log: Network connected
  1. 单例:
    • 每个类只能有一个伴生对象,且它是该类的唯一实例。
  2. 可具名:
    • 默认伴生对象的名称为 Companion,但可以为它指定其他名称。
class People(val name:String, val age:Int) {
    companion object Init {
        fun init(name: String,age: Int) = People(name,age)
        fun show() {
            println("People.show()")
        }
    }
}

在使用的时候,可以使用类名直接调用:

val p = People.init("阿狸", 18)
println("生成的对象:${p.name}")
People.show()

相关文章

网友评论

      本文标题:Android-Kotlin

      本文链接:https://www.haomeiwen.com/subject/fcvrejtx.html