上一节我们学习了Kotlin的一些基础语法包括和java语言的一些区别,虽然说java和kotlin是无缝对接,但是他们彼此之间相互调用还有一些需要注意的地方,
一、java与Kotlin互相调用
1、kotlin调用java时,需要做非空的判断处理,这样避免出现空指针异常
2、kotlin调用java的接口,可以使用(object : javaCallback )进行实例化后实现接口的方法
可以直接使用({这里显示接口方法的函数体}) 直接创建接口的的实例,并实现接口
3、java调用kotlin的方法,可以通过文件的名.方法名来进行调用,但是如果不想让java调用,可以使用双单引号将方法名字包含起来就无法调用了
fun 'show'(){ //此方法无法被java调用 }
4、kotlin中的扩展函数(就是可以在任何地方为一个类扩展一个函数)
class student{ } //此时为student添加了一个扩展函数 fun student.add(var number:Int,var number2:Int){ } fun main():Int{ student.add(1,2); }
5、kotlin泛型
1)java中泛型的通配符是? 而Kotlin泛型的通配符是 *
2)kotlin 中的out 相当于java是 extends 只读 输出
in 相当于java是 super 只写 输入
open class fuclass { var name1 = "fuclass" } class ziclass : fuclass() { var name = "ziclass" } fun main() { /** * TODO in out 的读写模式 */ var list: MutableList<out fuclass> = ArrayList<ziclass>() var temp = ziclass() // list.add(temp)//不能修改 报错 var item1 = list.get(0)//获取 var list2: MutableList<in ziclass> = ArrayList<fuclass>() // list2.add(fuclass()) var item = list2[0] println("name=${(item as ziclass).name},name1=${item.name1}") }
3) in out 在类的声明泛型时作用:可以控制整个类泛型读写模式, 而java中不能在泛型声明的时候限定泛型的读写模式
n 说明该类的泛型只能写 相当于输入,就是只写的 out 说明该类的泛型只能获取 相当于输出,就是只读的 不使用in out 说明该类的泛型既可以输入,也可以输出
//在声明泛型时候的泛型读写模式的限定 class Student<in T> { //可写 fun setData(data: T) { } //不可读,报错 fun getData(): T?{ return null } } class Teacher<out T> { //不可写,报错 // fun setData(data: T) { // } //可读 fun getData(): T? { return null } }
二、高阶函数
概念:将函数作为参数传递给主函数,该主函数称为高阶函数。
高阶函数在kotlin中随处可见,想要深刻理解高阶函数,需要先来学习一下lambda表达式,这样就让你学起高阶函数了达到事半功倍的效果。
2.1)lambda表达式
1、:() 括号中是参数
2、={} 括号中是函数体
3、var 变成val 后就不可以覆盖了
4、只有一个参数时可以不写(默认是it),多个参数则需要写清楚
//() 参数为空,返回值:Unit 函数名method , 该函数不能调用,因为没有函数体
var method:()->Unit
//有函数体,可以调用
var method1:(Int ,Int)->Int={number1+number2=number1+number2}
method1(9,9)
var method3={number1:Int,number2:Int-> number1+number2}
//只有一个参数
var m10 : (Int) -> Unit = {
when(it) {
1 -> println("你是一")
in 20..30 -> println("你是 二十 到 三十")
else -> println("其他的数字")
}
}
m10(29)
//无参数
var m12 = { println("我就是m12函数,我就是我") }
m12()
// 覆盖操作
var m14 = {number: Int -> println("我就是m14 我的值: $number")}
m14 = {println("覆盖 我的值: $it")}
m14(99)
2.2)高阶:
1、高阶函数的最后一个参数是一个函数时,在实现函数体时 可以在函数参数括号外使用{}来实现函数参数的函数体
2、使用typealias 来给高阶函数起一个别名 typealias LOGIN= (String, String) -> Unit 他就像一个数据类型一样的使用
3、当函数参数只有一个参数时,可以直接使用{}进行闭包,默认将一个参数命名为it,
有多个参数时,无法默认 则需要指明参数名,并用->指向函数体4、 如果函数参数已经有了实现体,那么可以通过:: 来直接调用;
:: 内部原理:就是将show_run函数的对象赋值给show_14的参数,那么说明show_run 是可以赋值给一个变量的
下面是typealias 的使用:
//类似于typedef
typealias LOGIN = (String, String) -> Unit
fun main(){
fun loginService(username: String, password: String, login: (String, String) -> Unit) {
login(username, password)
}
//高级别名的使用
fun loginService2(username: String, password: String, login: LOGIN) {
login(username, password)
}
//真正使用
fun loginEngine(username: String, password: String): Unit {
loginService(username, password) { username, pwd ->
//此处就是高阶函数的函数体
println("username=${username},pwd=${password}")
}
}
loginEngine("leon", "123456")
}
下面是一个或多个参数的使用:
fun main(){
// 多个参数的函数参数
fun show(mm: (String) -> Unit) {
mm("一个参数")
}
show {
println("一个参数的函数参数:$it")
}
fun show1(mm: (String, Int) -> Unit) {
mm("多个个参数", 1)
}
//下面是调用
show1 { param1, param2 ->
println("多个参数的函数参数:param1=$param1,param2=$param2")
}
}
下面是::来调用
fun main(){
// :: 来调用高阶的函数参数
fun show_14(number: Int, mm: (Int) -> String): String {
return mm(number)
}
fun show_run(number: Int) = "通过:: 来调用函数参数的实现函数_$number"
//
val showRunObj = ::show_run
println(show_14(50, showRunObj))
println(show_14(100, ::show_run))
}
2.3) 高阶里面的手写标准
1、kotlin中的泛型除了数据类型,还包含了方法,方法也算一个类型,即万能类型
2、T 是泛型,R是返回类型的泛型
3、T.MyRun相当于给泛型T增加了一个MyRun的扩展函数, mm: T.() -> R 代表给T增加一个匿名的扩展函数
4、类似于RxJava中的链式调用
fun main(){
fun <T, R> T.MyRun(mm: () -> R): R {
return mm()
}
//给T增加一个匿名函数 T.()
fun <T, R> myWith(input: T, mm: T.() -> R): R {
return input.mm()
}
var age = 0
age.MyRun {
"headworld"
}
var name = "Leon"
myWith(name) {
length//此时可以直接获取name的长度
}
}
2.4 高阶里面的标准特色:let、apply 、also、run、with、takeIf、takeUnless、repeat
kotlin给我们提供了很多类似的标准,可以参考Standard.kt里面的自己去学习,
let : 其实就是将调用类型的对象自己作为参数传递给block的函数体
apply: 其实就是在执行完block函数后,将apply的调用者返回,类似于建造者模式的链式调用
also:将当前调用者作为参数传递给block函数,并将调用者作为返回值返回,便于链式调用
run: 将调用者作为参数传递给block函数,并将block执行的结果返回
5)with:使用指定的receiver作为接受者调用block函数后,将block函数执行的结果返回
6)takeIf:当调用者满足predicate的条件时返回调用者,不满足时返回null
takeUnless: 和takeIf正好相反
7)repeat: 其实就是系统提供出来的一个轮询器,方便我们遍历数组
var Leon = "Leon"
Leon.let {
println("let 参数it的值是${it}")
}
var temp = Leon.apply {
println("apply 回传的this的值是$this")
}
println("apply 执行后的结果$temp")
repeat(Leon.length) {
println("repeat轮询:it=${it},leon的当前位置的字符是:${Leon[it]}")
}
//输出的结果如下:
//let 参数it的值是Leon
//apply 回传的this的值是Leon
//apply 执行后的结果Leon
//repeat轮询:it=0,leon的当前位置的字符是:L
//repeat轮询:it=1,leon的当前位置的字符是:e
//repeat轮询:it=2,leon的当前位置的字符是:o
//repeat轮询:it=3,leon的当前位置的字符是:n
虽然kotlin给我们提供了很多标准,其实我们还是可以自己定义我们所需要的的标准的,下面来模仿一下系统的repeat 做一个自定义
说明:
1)我们在使用下标的时候,下标从0 开始,所有这里在执行轮询的时候,需要使用区间,但是必须until 去掉尾部,否则会多执行一次
2)step 后面的步长,必须要明确说明,不能作为参数来进行赋值
3)真正的操作是在action这个函数参数的函数体内完成
fun main(){
fun doWhile(count: Int, action: (Int) -> Unit) {
for (index in 0 until count step 1) {
action(index)
}
}
doWhile(10) {
println("自定义轮询器的下标:$it")
}
}
//输出结果如下:
自定义轮询器的下标:0
自定义轮询器的下标:1
自定义轮询器的下标:2
自定义轮询器的下标:3
自定义轮询器的下标:4
自定义轮询器的下标:5
自定义轮询器的下标:6
自定义轮询器的下标:7
自定义轮询器的下标:8
自定义轮询器的下标:9
2.5)自定义线程的封装
1)我们分封装的线程,这样后续我们可以直接值关心耗时操作即可,
2) openThread 可以作为一个模板来使用
fun openThread(start: Boolean, RunTask: () -> Unit): Thread? {
val thread = object : Thread() {
override fun run() {
super.run()
RunTask()
}
}
return if (start) {
thread.start()
thread
} else {
null
}
}
fun main(){
//对比系统的thread代码,大体类似
openThread(true) {
println("我是在子线程中执行的耗时操作:输出字符串")
}
}
今天我们主要学习了一下kotlin和java互相调用时一些需要注意的点,着重学习了kotlin中的高阶和自定义标准模板,希望对各位看官有所帮助。