先来了解下集合初始化的方式
listOf创建的集合是一个不可变的集合,也就是不能调用添加或者删除的方法。
//声明一个list
val list= listOf("唐三","小舞","马红俊","戴沐白","朱竹清","奥斯卡","宁荣荣")
下面创建的集合是可变的。类似的set集合的创建方式是一样的,set集合不允许重复元素。对应的方式是setOf、mutableSetOf
val list= mutableListOf("唐三","小舞","马红俊","戴沐白","朱竹清","奥斯卡","宁荣荣");
list.add("白沉香")
map 的声明和添加数据方式,以及遍历数据。
val map=HashMap<String,Int>()
map["唐三"]=1;
map["小舞"]=2;
map["戴沐白"]=3;
for ((name,number) in map){
println("name is $name,number is $number")
}
也可以这样定义初始化map集合
//这个方式定义的map同样是不能修改的
val map = mapOf("唐三" to 1,"小舞" to 2,"戴沐白" to 3)
跟list一样,如果需要修改map的数据,像这样定义map,就可以添加或者删除数据
val map= mutableMapOf("唐三" to 1,"小舞" to 2,"戴沐白" to 3)
map["朱竹清"]=4
下面来看Lambda的使用
Lambda 就是一小段可以作为参数传递的代码,这一小段代码不宜过长,否则会影响可读性。
Lambda语法结构{参数名1:参数类型,参数名2:参数类型->函数体},最后一行代码会自动作为Lambda表达式的返回值。
//获取长度最长的人名
val list= mutableListOf("唐三" ,"小舞" ,"戴沐白" )
val maxLength=list.maxByOrNull { name : String -> name.length }
println("maxLength is $maxLength")
由于Kotlin出色的类型推到机制,Lambda表达式中参数的类型可以省略,上面的代码可以简化
//由于出色的类型推到机制,上面的类型可以简化
val maxLength=list.maxByOrNull { name -> name.length }
当Lambda表达式的参数列表只有一个的时候,也可以不必声明参数名,而是可以使用it代替,进一步简化
val maxLength=list.maxByOrNull { it.length }
上面的这种方式就是函数式API语法结构
集合中的map函数是最常用的一种函数式API,它用于将集合中的每一个元素都映射成另外一个值,映射的规则在Lambda表达式中指定,最终生成一个新的集合
val list= listOf("tang san","xiao wu","dai mu bai","zhu zhu qing","ao si ka")
val newList=list.map { it.uppercase(Locale.CHINA) }
for (name in newList){
println("name is $name")
}
另外一个函数式API-filter,它是用来过滤集合中的数据的,它可以单独使用也可以配合map一起使用。
val list= listOf("tang san","xiao wu","dai mu bai","zhu zhu qing","ao si ka")
val newList=list.filter { it.length>8 }.map { it.uppercase() }
for (name in newList){
println("name is $name")
}
先使用filter进行条件筛选,然后再转成大写,顺序可以调换。
函数值API–any 和 all,any表示是否存在,all表示是否全部是,这两个是条件判断,返回true and false
//函数值API any all
val list= listOf("tang san","xiao wu","dai mu bai","zhu zhu qing","ao si ka")
val any = list.any { it.length > 8 }
val all = list.all { it.length > 8 }
println("any is $any , all is $all") //any is true , all is false
java函数式API调用
如果在kotlin中调用一个java方法,并且该方法接收一个java单抽象方法接口参数,就可以使用函数式API。
Thread { println("do some thing") }.start()
Thread 里边需要一个参数 Runnable ,Runnable里边只有一个抽象run方法,所以可以直接省略这些。
val editText = EditText(context)
editText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun afterTextChanged(editable: Editable) {}
})
参数有多个抽象方法需要实现,不能省略,书写格式是这样的。
空指针检查
下面的函数,当调用的时候,是不能传递null参数的,直接会编译不过去
fun readBooks(study: Study){
study.readBooks()
}
Kotlin利用编译判空检查机制几乎杜绝了空指针的异常,如果业务逻辑需要传null的场景,可以定义可以为null的参数类型
override fun onClick(v: View?) {
when (v?.id) {
R.id.iv_create_pci_finish -> picFinish()
}
}
这样就可以传null了,但是这样写依然编译不过去,因为传递的参数可能会null就需要添加null判断
Int? 可为null的整形 Int 不可为null的整形
String表示不可null的字符串 String?可为null的字符串
?.操作符
这个操作符表示不为null正常调用某个方法,当对象为null则什么都不做。
?:操作符
这个操作符两边都接收一个表达式,如果左边的表达式不为null就返回左边的表达式,否则返回右边的表达式
val c = a ?: b
//这个表达式相当于
val c = if (a != null) {
a
} else {
b
}
!!操作符
如果一个对象可能会null,这个时候这个对象调用某个方法编译不会通过,如果不想添加判null操作,可以使用这个操作符!!,表示告诉kotlin,这个对象我非常确信不为null (感觉这个操作符没啥用,直接加判null就好了)
//参数是一个可能会null的对象,这个时候如果不想添加判null需要编译通过就需要使用!!这个操作符
mVideoFragment!!.seekTimeline(
timestamp,
NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_ANIMATED_STICKER_POSTER
)
let函数,属于kotlin的标准函数
let函数提供了函数式API的编程接口,并将原始调用对象作为参数传递到Lambda表达式中,**let函数配合?.**操作符非常好用
mTimelineEditor?.let {
it.setSequencLeftPadding(sequenceLeftPadding)
it.setSequencRightPadding(halfScreenWidth)
it.setTimeSpanLeftPadding(sequenceLeftPadding)
}
字符串的内嵌表达式
之前打印数据的时候已经涉及到了,比如之前的打印表达式
"any is $any , all is $all"
函数的参数默认值
fun test(num:Int,name:String="Tom"){
println("num is $num, name is $name")
}
test(1) //这样调用是合法的
这个函数第二个参数有一个默认值,那么调用这个函数的时候,可以只传递第一个参数,也可以两个都传递
fun test(name:String="Tom",num:Int){
println("num is $num, name is $name")
}
test(num=1) //通过键值对的方式传递参数就可以调用了
如果第一个参数是带默认值,直接像test(1)就会报错,kotlin认为配型不匹配。这个问题可以使用键值对的方式进行传值,这个也是允许的
Kotlin 另外几个常用的标准函数
with、run、aplly这几个标准函数在开发中会经常使用到,kotlin中的标准函数是指在Standard.kt文件中定义的函数,任何Kotlin的代码都可以随便调用使用。
with函数接收两个参数,第一个参数是任意类型的对象,第二个参数是一个Lambda表达式,with函数会在Lambda表达式中提供第一个参数的对象的上下文,并使用Lambda表达式最后一行的表达式作为返回值。
NvsStreamingContext.SdkVersion sdkVersion = mStreamingContext.getSdkVersion();
StringBuilder stringBuilder = new StringBuilder("V ");
stringBuilder.append(sdkVersion.majorVersion);
stringBuilder.append(".");
stringBuilder.append(sdkVersion.minorVersion);
stringBuilder.append(".");
stringBuilder.append(sdkVersion.revisionNumber);
mSDKVersion.setText(stringBuilder.toString());
上面是sdk demo中的代码,如果使用with函数进行改造
NvsStreamingContext.SdkVersion sdkVersion = mStreamingContext.getSdkVersion();
val result=with(StringBuilder()){
append("V ")
append(sdkVersion.majorVersion)
append(".")
append(sdkVersion.minorVersion)
append(".")
append(sdkVersion.revisionNumber)
toString()
}
mSDKVersion.setText(result);
使用with函数代码变的更加精简
run函数
这个函数跟with函数类似,得到的结果也相似。
val result=StringBuilder().run{
append("V ")
append(sdkVersion.majorVersion)
append(".")
append(sdkVersion.minorVersion)
append(".")
append(sdkVersion.revisionNumber)
toString()
}
mSDKVersion.setText(result);
aplly函数
这个函数跟run函数类型,但是最后一行不是返回值,而是返回对象本身,下面是一个实际应用的例子。
mStartNextActivity.setOnClickListener {
val intent=Intent(this,OtherActivity::class.java).apply {
putExtra("param1",1)
putExtra("param2","2")
putExtra("param1",1)
}
startActivity(intent)
}
kotlin中定义静态方法
相比于java,kotlin中因为单例很简单,弱化静态方法的概念。像java中的工具类Kotlin中推荐直接使用单例进行实现。
object Utils {
private const val MIN_DELAY_TIME = 1000
private var lastClickTime: Long = 0
/**
*两次点击间隔不能少于1000ms
* The interval between two clicks cannot be less than 1000ms
*/
fun isFastClick(): Boolean {
var flag = true
val currentClickTime = System.currentTimeMillis()
if (currentClickTime - lastClickTime >= MIN_DELAY_TIME) {
flag = false
}
lastClickTime = currentClickTime
return flag
}
}
这样处理之后,里边所有的方法就可以像静态方法那样调用了。
如果某个类里边,有对象方法也想要静态方法怎么办?可以使用companion object
class Utils {
companion object{
private val MIN_DELAY_TIME = 1000
private var lastClickTime: Long = 0
/**
*两次点击间隔不能少于1000ms
* The interval between two clicks cannot be less than 1000ms
*/
fun isFastClick(): Boolean {
var flag = true
val currentClickTime = System.currentTimeMillis()
if (currentClickTime - lastClickTime >= MIN_DELAY_TIME) {
flag = false
}
lastClickTime = currentClickTime
return flag
}
}
fun doAction(){
Log.e("Utils","doAction")
}
}
像上面这样编写就能解决上面的问题,isFastClick这个方法仍然可以像静态方法那样调用,doaction就是一个对象方法。但是这样处理在原理上isFastClick并不是一个静态方法,companion object这个关键字会在Utils类中创建伴生类,kotlin保证只会存在一个伴生类。
如果想要定义真正意义的静态方法,kotlin提供了两个方式:注解和顶层方法。
companion object只是在语法上模拟了静态方法的调用方式,如果加上@JvmStatic注解,那么kotlin编译器会将这个方法编译成真正的静态方法。
class Utils {
companion object{
private val MIN_DELAY_TIME = 1000
private var lastClickTime: Long = 0
/**
*两次点击间隔不能少于1000ms
* The interval between two clicks cannot be less than 1000ms
*/
@JvmStatic
fun isFastClick(): Boolean {
var flag = true
val currentClickTime = System.currentTimeMillis()
if (currentClickTime - lastClickTime >= MIN_DELAY_TIME) {
flag = false
}
lastClickTime = currentClickTime
return flag
}
}
fun doAction(){
Log.e("Utils","doAction")
}
}
这样isFastClick就是真正意义上的静态方法了。@JvmStatic 这个注解不能加在普通方法中会报错。
kotlin顶层方法
顶层方法是指,没有定义在任何类中的方法。在kotlin中创建Kotlin file ,在file中定义的方法都是静态方法,全局可以调用也是真正意义的静态方法,全局可以调用。
延迟初始化
private var mTimeline: NvsTimeline? = null
这是声明全局变量的方式,如果这样初始化,那么在使用mTimeline这个对象的时候需要频繁的使用?.进行判空操作,否则编译不过去,由于kotlin编译机制就不得不编写大量额外的判空操作。
如何解决这个问题呢?延时初始化lateinit使用这个关键字修饰全局变量,这样声明的变量不为null,使用的时候可以避免频繁的判空操作。
private lateinit var mTimeline: NvsTimeline
但是这个关键字本身是有风险的,如果使用之前没有初始化会抛异常,所以当对一个全局的变量使用了lateinit关键字,请确保它在被任何地方调用之前已经初始化。
在使用全局变量之前可以通过代码来判断全部变量是否已经初始化,::mTimeline.isInitialized这个就是判断对象是否会null的方法。
if(!::mTimeline.isInitialized){
mTimeline=initTimeline()
}
扩展函数
扩展函数表示在不修改某个类源码的基础上,仍然可以打开这个类,向该类添加新方法。
扩展函数的语法结构
fun ClassName.methodName(param1:Int,Param2:Int):Int{
return 0;
}
扩展函数可以放在顶层函数,这样扩展函数就拥有了全局的访问域。
fun String.lowersCount():Int{
var count=0
for (low in this){
if (low.isLowerCase()){
count++
}
}
return count
}
//测试方法
val lowNum="asdWERff".lowersCount()
println("lowNum is $lowNum ")
//输出日志
//lowNum is 5
上面是给String定义的一个统计小写字母个数的方法,这个在String类里边是没有的。利用这个特性,可以写出丰富多样的扩展函数,非常好用。扩展函数在kotlin中没有任何限制,可以再任何类上添加扩展函数,这将大大提升代码质量和研发效率。