内容简介:
主要帮助解决很多人写kotlin代码的时候 主要依赖ide提示来完成代码的问题,这是一个不太好的习惯,虽然可以编译成功,甚至功能看起来正常,但是容易埋坑,一定要知道为什么ide 提示你这样做。
另外kotlin目前还是和java在一起混合使用的比较多。本文会介绍kotlin的部分注解,让java可以顺利按照java的想法调用kotlin的代码。
类的构造器
class Person constructor(var age: Int, var name: String)
//很kotln的写法 如果参数加上var 或者val 那就是属性 ,不加的话 就仅仅是个参数了
//例如这里params 就是个参数 但是age 和name就是属性
class Person2(var age: Int, var name: String, params: Int)
//params 在init 这个主构造器里面是可见的
class Person3(var age: Int, var name: String, params: Int) {
var pvalue: Int
init {
pvalue = params
}
}
//这里面构造器123 3个加起来就是这个person4 初始化的时候构造函数里面的调用了
//init块 是可以多个的 反正最后都会合并
class Person4(var age: Int, name: String) {
var name: String
//构造器1
init {
this.name = name
}
//这个也算 构造器2
val firstName = name.split(" ")[0]
//构造器3
init {
}
}
复制代码
当然你可以选择不定义主构造器,但是这么做非常不好,这会导致你的类初始化路径出现多条。 这是非常不推荐的设计模式,想想你平常java代码里面有没有这么写。如果有 记得改正。
可以看看kotlin中不推荐的写法,但是可以编译通过。
同样的kotlin的这种能力还可以提供给java
在java中调用
当你如果把注解取消掉的时候,那么java代码就无法调用Person10(int age) 这个构造函数了。 这个地方要细细体会一下。
接口代理
这里需要你对代理模式有一定了解。 这里就不再说了,大家可以看一下最后一种写法 可以省略多少代码。
interface Api {
fun a()
fun b()
fun c()
}
class ApiImpl : Api {
override fun a() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun b() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun c() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class ApiWrapper(private val api: Api) : Api {
override fun a() {
api.a()
}
override fun b() {
api.b()
}
override fun c() {
println("ccccc")
api.c()
}
}
//这种写法 就相对于上面的写法 要简单许多
class ApiWrapper2(private val api: Api) : Api by api {
override fun c() {
println("ccccc")
api.c()
}
}
复制代码
整体来说 接口代理用的不是特别多,大家知道有这么个语法糖就好
属性代理
属性代理用的就比较多了。
接口Lazy的实例代理了对象Person的实例的属性 firstName的getter
再比如说 可以很方的用observable来监听某个属性的变化。真是超级好用。。
class StateManager {
var state: Int by Delegates.observable(0) { property, oldValue, newValue ->
println("state changed $oldValue ->$newValue")
}
}
复制代码
延迟初始化
lateinit 其实不建议用,因为lateinit就会导致你kotlin的空安全失效了。 这就是lateInit最恶心的地方。 虽然大部分人看到的kotlin android代码里面 activity 里面的那些 控件 都是用lateinit来做 声明。
虽然kotlin 1.2 中提供了一个api来处理 是否初始化的逻辑,但是也不建议使用。
这里推荐使用lazy 来做初始化
private val nameTextView by lazy{
findViewById<TextView>(R.id.textview)
}
复制代码
而且 声明和初始化放在一起 是最好的写法。
kotlin单例与java 混用
kotlin中的单例可能是kotlin中最好的一个语法糖了,使用起来比java 那堆东西简单多了。但是往往我们在写的时候还要给java调用一下。
毕竟我们现在主要的工程都是kotlin与java 混合在一起的。
object Singleton {
var x: Int = 2
fun y() {
}
}
复制代码
看下在java中如何调用
在这里其实可以看出来,在kotlin中实际上是没有静态成员这个概念的,因为kotlin的野心极大, 他想做跨平台的语言,其他语言往往是没有静态成员这个概念的。
我们甚至还可以把get和set去掉
object Singleton {
@JvmField
var x: Int = 2
@JvmStatic
fun y() {
}
}
复制代码
这里再说一个伴生对象的概念。
比如说我们有时候java代码会这么写:
class Foo{
public static void y(){}
}
复制代码
在kotlin中 就可以这么写
class Foo{
companion object{
@JvmStatic fun y(){}
}
}
复制代码
这个就叫做伴生对象了。
另外就是
kotlin中的object 你就把他当做是一个java中的饿汉单例,仅此而已
内部类
这个地方一定搞清楚差别,因为普通的内部类 会持有外部类的引用,容易引发内存泄漏。
java的写法
public class Outer{
class Inner{}
static class StaticInner{}
}
复制代码
再看看kotlin中的写法
class Outer{
inner class Inner //这个是非静态的内部类
class StaticInner //这个是静态的内部类
}
复制代码
kotlin使用内部类和在java中也是一样的。
kotlin中枚举类的使用
枚举类在kotlin中的使用 与java是基本一致的。只有少数部分不一样。 例如 kotlin中是可以为枚举定义扩展的
enum class State:Runnable{
Idle,Busy,Error;
override fun run() {
println("every state run")
}
}
//取下一个枚举的值
fun State.next():State{
return State.values().let {
val nextOrdinal=(ordinal+1)%it.size
it[nextOrdinal]
}
}
复制代码
println(State.Idle.next())
println(State.Busy.next())
println(State.Error.next())
复制代码
应该就可以理解了。当然严格意义上来说 这是属于kotlin中 扩展函数的能力了。
kotlin中 枚举多数是和when表达式在一起使用的。
val state=State.Error
println(when(state){
State.Idle->{"get idle"}
State.Busy ->{"get busy"}
State.Error->{"get error"}
})
复制代码
既然是枚举,所以肯定是可以穷尽的对吧,所以当他和when表达式在一起的时候,他是可以省略else分支的。
枚举的区间
比方说扑克牌。
enum class Poker{
J,Q,K,A
}
复制代码
可以直接使用区间来判断他们
val pokerRange=Poker.J..Poker.K
println(Poker.J in pokerRange)
println(Poker.Q in pokerRange)
println(Poker.K in pokerRange)
复制代码
密封类
他有3个特点; 1.密封类是一个抽象类 2.密封类的子类必须与密封类定义在同一个文件中。 3.密封类的子类个数是有限的。
这里的第三点很多人无法理解,其实很好理解,因为你密封类的子类必须和密封类在一个文件中,所以实际上密封类的子类是不可以无限扩展的,那你当然子类个数就是有限的了、
为什么外部无法继承密封类?
来看个例子:
sealed class PlayerState
constructor(val state:Int,val message:String)
object Idle:PlayerState(2,"idle")
class Error(var errorNo:Int):PlayerState(1,"error")
复制代码
然后我们反编译看一下:
image.png
构造器都是私有的,密封类的子类还都是final类的,那当然外部无法继承了
密封类 与when在一起使用
val state:PlayerState=Idle
println(when(state){
Idle->{
"idle"
}
is Error->{ "error"}
is Playing->{"playing"}
})
复制代码
可以观察一下,密封类其实主要还是类型可数的,而枚举只能是值可数 这是两者最大的区别。 换句话说 密封类是用来标定类型差异的,而枚举是用来标定值差异的。
数据类
这个地方要着重提一下,很多人都觉得 kotlin中的data class 有啥好说的?不就是类似于java中的javabean么?
一定要切记:kotlin中的data class 与java bean 并不相等!
编译器在编译data class的时候 会自动生成equals hashcode toString 以及 copy 方法。
这里注意copy 方法是一个浅拷贝方法。
此外,数据类 编译以后 在java中要用component1 的形式调用。
data class Book2(val id: Long, val name: String, val author: String)
复制代码
反编译看下 就知道为何要用com的形式调用了。
数据类建议大家 就写成上述最简单的写法,不要做任何扩展
最后dataclass 相关的还有2个插件,在这里介绍一下,感兴趣的同学 可以执行搜索一下。
noArg 插件 可以 让kotlin生成的data class 拥有 无参的构造器,但是因为是编译期生成的,所以你想要调用他 必须在代码中利用反射来调用。
allOpen插件。可以改变final的特点。
来源:掘金作者:DK_BurNIng
链接:https://juejin.im/post/5e8f168d6fb9a03c2f4e0ecf
各位看客老爷点个关注,或者转发,每日更新Android知识干货,一起学习、共同进步!
网友评论