4月份,因负责的项目变更,开始了纯kotlin编码。以下是我在开发过程中总结的kotlin体会,希望对上手时间不长的朋友有帮助。
变量声明
- val
不可变变量声明
val people:People = People()
val age:Int= 29
people=People() //提示错误
age=28 //提示错误
- var
可变变量声明,需要设置初始值
var people:People = People()
people=People() // 正常
- lateinit
可变变量延迟初始化
private lateinit var name :String
空安全
(String )非空
var name:String="wenjie"
fun setNamne(nameTp:String){
name=nameTp
}
/*调用*/
setName(null) //提示错误,非空不能接受为null的值,如果是运行期间的赋值,那将会发生崩溃。
(String)可为空
var name:String?="wenjie"
/*调用*/
name=null //√
val length=name.length() //提示错误 可空声明不能直接调用
val length=name?.length() //√
操作域语法糖
let
常用来调用可为空对象时使用
var id:String?=""
/*idb不为空,则执行逻辑 一级内容设置*/
id?.let{
doSomething(it)
}
/**若let嵌套使用,则往往无法分辨it指代的是谁,可以通过lambda表示式区分*/
id?.let{
doSomething(it)
xName?.let{ xNameIt- >
doSomething2(xNameIt)
}
}
apply
常用来给一个对象设置很多属性 ,一般用于配置
val people :People= People()
people.apply{
setName("name")
setAge(29)
setLike("read")
}
run
可与let作用相同使用,但其{}lambda表达式内返回的是 语句执行的结果
val str:String? = "hello world"
str?.run{
toUpperCase()
}.run{
println(this)
}
类
class
声明类,普通类无法被继承
/*普通类*/
class Block{}
data class
数据类,专门用于生成自定义数据的类,省去了get set方法的代码;也更单一职责,不易与其他类混淆
/**声明一个数据类.
1.()内声明的属性均具有getter setter方法。
2.可设置默认值,可不设置
3.默认值是public访问权限的
*/
data class Datas(var name1:String="",
var age1:Int=9,
var people:People?,
var like:Like)
object
单例类声明
1.类中所有方法均为静态的,通过类名直接可以访问
2.声明静态变量不需要companion object {}域的声明
object class ActivityManager{
}
interface
声明接口
interface IHappy{
/*默认为public 权限,子类需要实现*/
fun howToHappy()
/*有{}实现的,子类不需要强制实现*/
fun whyHappy(){}
/*带返回值,带参数的声明*/
fun hasHapppyPower(userName:String):Boolean
}
open
类的修饰符,放置在class 前;代表此类打开了被继承的权限;可以有子类继承
open class View{}
inner class
内部类对应java中的非静态内部类。
class Abc{
inner class Bcd{
fun testInnerSome(){}
}
}
fun test(){
Abc().Bcd().testInnerSome()
}
表达式
if else
-
普通用法与java一致
if(a){
}else{
}
-
赋值用法,可直接赋值给变量
val result= if(x){ 20 }else{ 30 }
/常见多元判断条件/ if(a){
}else if(){
}else{
}
when
-
类似于switch case,是一种lambda风格语法
when(x){ 1-> {print("x1")} 2-> {print("x2")} else ->{print("else")} }
-
枚举和密封类可以不需要else,因为编译器检测范围确定
enum class Bit{ ZERO, ONE }
val result=when(getBit()){ Bit.ZERO -> 0 Bit.ONE -> 1 }
-
可以使用任意表达式作为分支条件
when(x){ x+b==2->{} else->{} }
-
取代if-else if 链(所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支)
when{ x.isOk()->{} x.isHappy()->{} else ->{} }
for
for(item in collList){
}
/*数字区间表达式*/
for(i in 1..9){
}
/*对于数组的遍历*/
val array = arrayOf("1","2","3")
for( i in array.indices){
println(array[i])
}
while
与java一致,不太常用
while(x>0){
}
do{
y--
}while(y==0)
集合
-
list 声明
/可变的/ val numbers = mutableListOf("one","two","three") val numbers = arrayListOf("one","two","three")
/不可变/ val numbers = listOf("one","two","three")
-
map 声明
/不可变/ val maps = mapOf<Int,String>(2 to "b",1 to "a",3 to "c")
/可变/ val mutableMaps= mutableMapOf<Int,String>(2 to "b",1 to "c",3 to "x")
hashMapOf linkedMapOf
/遍历/ maps.forEach{(k,v)-> println("v") }
高阶函数编程
高阶函数是将函数用做参数或者返回值的函数
fun <T,R> Collection<T>.fold(
initial:R,
combine:(acc:R,nextElement:T)->R
):R{
var accumulator:R = initial
for(element:T in this){
accumulator = combine(accumulator,element)
}
return accumulator
}
扩展函数
-
扩展函数可以对现有的类进行拓展一些很方便的方法,比如工具类,view操作类,可以减少模版代码
-
扩展并不能真正的修改某个类的方法,是一种包装延伸
fun String.hasHappyElement():Boolean{ ... }
fun View.hide(){ visibility=View.GONE }
-
扩展函数可以重载,但不能重写某个类的方法
/重载/ fun main() {
class Example { fun printFunctionType() { println("Class method") } } fun Example.printFunctionType(i: Int) { println("Extension function #$i") } Example().printFunctionType(1) //结果输出Extension fun...}
/触发重写或本意是重写/ fun main() {
class Example { fun printFunctionType() { println("Class method") } } fun Example.printFunctionType() { println("Extension function") } Example().printFunctionType() //结果返回 Class method}
-
扩展属性(因为扩展属性不能将属性加入到类中,因此无法有初始化器。只能由显示提供的getter/setter提供)
val House.number :Int =1 //错误,不能拥有初始化器
val List.lastIndex:Int get()=size-1 //正确
-
伴生对象的扩展
class MyClass { companion object { } // 将被称为 "Companion" }
fun MyClass.Companion.printCompanion() { println("companion") }
fun main() { MyClass.printCompanion() }
-
扩展的作用域
一般情况在顶层定义扩展,无需导入。
也可以定义在外部,使用时导入包即可。
值比较
==:结构相等性操作符
- 该操作符用于检查两个变量或对象的值或内容是否相等。
- 通常用于比较基本数据类型,因为它会比较传递的对象或变量的值。
- 例如,4 == 4 返回true,因为它比较的是值。
===:引用相等性操作符
- 该操作符用于检查两个变量或对象是否指向相同的内存位置。
- 简单来说,两个变量必须指向同一个对象。
- 如果对基本类型(如Int、Double)使用===,则===将转换为==,即它将比较内容而不是检查相同的引用。
字符串拼接
+操作符、plus() :或多创建一个字符串对象
val a="33
val b="dd"
val c="$a"+"$b"+"happy
字符串模版
val a="33
val b="dd"
val c="$a $b"
StringBuilder
val sb = StringBuilder()
sb.append("Hello")
sb.append(" ")
sb.append("World")
val c = sb.toString()
println(c)
类型检测与转换
-
is操作符 !is
if(obj is String){ print(obj.length) }
if(obj !is String){ print("Not is String") }
inline
-
内联函数可以在编译后减少函数体{}Function0创建的内存开销
/不使用内联/ fun main(args:Array){ foo{ System.out.print("hello") } }
fun foo(block:()->Unit){ block() }
//实际调用时(java): foo((Function0)null.INSTANCE) block.invoke()
/使用内联后/ String var1="hello" System.out.print(var1) //没有额外创建对象,只是对闭包内的函数体进行了粘贴
-
一般非高阶函数不需要内联优化,反而会使字节码变的复杂
-
避免对具有大量函数体的函数进行内联,会导致更多的字节码
-
一旦内联后,不能获取闭包的私有成员