kotlin使用记录

https://kotlinlang.org/docs/reference/android-overview.html
学习资料

github>>kotlin
courses/

中文入门语法

记录

let, also,apply,run

https://blog.csdn.net/u013064109/article/details/78786646
简单分析下,also和apply一样都是返回自己, let和run一样返回花括号代码块最后一行的返回值
另外一样的2种区别就在于代码块里用it还是this代替变量,而this可以省略不写。

image.png

如上图,结果就是 2个hello,以及7和5
简单看下源码

public inline fun  T.apply(block: T.() -> Unit): T { block(); return this }
public inline fun  T.also(block: (T) -> Unit): T { block(this); return this }

public inline fun  T.let(block: (T) -> R): R = block(this)
public inline fun  T.run(block: T.() -> R): R = block()

看到源码可能感觉和我们的写法不一样。其实原因很简单,kotlin里一个函数可以作为参数的,完事这个函数可以写到括号外边。
比如 T.apply({//函数块}) 可以写成 T.apply(){//函数块}
对于let和run,如果要主动return一个东西,这样写的// return@run 333

在看demo的时候看到这些代码,扩展函数

可以注意到方法名字前有个xxx. 的东西,这个就是声明对哪个类进行扩展

/**
 * The `fragment` is added to the container view with id `frameId`. The operation is
 * performed by the `fragmentManager`.
 */
fun AppCompatActivity.replaceFragmentInActivity(fragment: Fragment, @IdRes frameId: Int) {
    supportFragmentManager.transact {
        replace(frameId, fragment)
    }
}

/**
 * The `fragment` is added to the container view with tag. The operation is
 * performed by the `fragmentManager`.
 */
fun AppCompatActivity.addFragmentToActivity(fragment: Fragment, tag: String) {
    supportFragmentManager.transact {
        add(fragment, tag)
    }
}

fun AppCompatActivity.setupActionBar(@IdRes toolbarId: Int, action: ActionBar.() -> Unit) {
    setSupportActionBar(findViewById(toolbarId))
    supportActionBar?.run {
        action()
    }
}

/**
 * Runs a FragmentTransaction, then calls commit().
 */
private inline fun FragmentManager.transact(action: FragmentTransaction.() -> Unit) {
    beginTransaction().apply {
        action()
    }.commit()
}

代码里这样用的

        setupActionBar(R.id.toolbar) {
            setHomeAsUpIndicator(R.drawable.ic_menu)
            setDisplayHomeAsUpEnabled(true)
        }

其实原本应该这样的,不过最后一个参数是一个函数的话,可以直接把括号的实现提到圆括号外面,就成了上边的写法了

        setupActionBar(R.id.toolbar, {
            setHomeAsUpIndicator(R.drawable.ic_menu)
            setDisplayHomeAsUpEnabled(true)
        })

when

java 中的switch 在kotlin里的写法,

when(xxx){
            is Int->{
            }
            in 0..10->{
            }
 1->{
}
2->{
}
3,4->{}
else->{}
}

2个方法互相引用的时候

java代码如下,我是2个runnable 互相调用

    private void starte(){
        handler.postDelayed(r1,1000);
    }
    Handler handler=new Handler();
    
    Runnable r1=new Runnable() {
        @Override
        public void run() {
            System.out.println("==========1");
            handler.postDelayed(r2,1111);
        }
    };
    
    Runnable r2=new Runnable() {
        @Override
        public void run() {
            System.out.println("==========2");
            handler.postDelayed(r1,1111);      
        }
    };

kotlin代码如下

    internal var handler = Handler()

    internal var r1: Runnable = Runnable {
        println("==========1")
        handler.postDelayed(r2, 1111)
    }

    internal var r2: Runnable = Runnable {
        println("==========2")
        handler.postDelayed(r1, 1111)
    }

    private fun starte() {
        handler.postDelayed(r1, 1000)
    }

看下转化,加了internal,这个是限定同一模块下可以访问,非必需。
关键有的地方 r2: Runnable 这个,2个Runnable必须有一个需要加冒号声明类型,否则就报错。
Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly
中文:类型检查遇到了递归问题。最简单的解决方法:显式地指定声明的类型。

可以看到上边都是简化后的写法了。如果这个时候要使用this,就不行了,得写完整的
如下

    internal var r2: Runnable = object : Runnable {
        override fun run() {
            println("==========2")
            handler.postDelayed(r1, 1111)
            handler.postDelayed(this, 2222)
            Runnable { handler.postDelayed(this, 2222) }.run()
        }
    }

循环的几种写法

下边的代码结果都一样,都是从0到4循环的,从大到小的话可以用 downto关键字

    private fun forTest(){
        //循环N次,from 0 start
        repeat(5){
            println("a============$it")
        }
        for(i in 0 ..4){
            println("b==========$i")
        }
        for(i in 0 until 5){
            println("c==========$i")
        }
        (0..4).forEach {
            println("d==============$it")
        }
    }

set get

//set的简化写法
     var tasks: List = tasks
            set(tasks) {
                field = tasks
                notifyDataSetChanged()
            }

  //get的简化写法
    override var isActive: Boolean = false
        get() = isAdded

thread ,简化写法如下,不需要start,内部默认构造方法里start为true的。

thread {

}

问题记录

1~
java 代码 Object obj=new Object(); obj.wait();
kotlin里的Any()对象没有wait()方法,不知道转成kotlin之后咋写。
解决办法,kt里继续new一个Object对象而不是any对象。
https://www.jianshu.com/p/3963e64e7fe7
2~

双冒号::

可以理解为把一个方法当对象处理,如下

    class A {
       lateinit var b:B
        init {
            b=B().apply {
                this.callbackA=this@A::testA//把testA这个方法传递给B里边的一个变量callbackA了
            }
        }
        fun testA(name: String,age:Int){

        }
    }
    class B{
        var callbackA:((String,Int) ->Unit)?=null//这里对应写出参数的类型,返回的类型好匹配,因为是可以为空的,所以括起来加个问号
        fun testB(){
            callbackA?.invoke("jerry",33)
        }
    }

上边的可以用来写我们平时用的Callback。
如果是java,我们一般是定义一个interface,完事定义一个interface的变量,然后用的时候实例化一个interface调用
然后看下改版后的写法
如下自定义的类

class ItemTouchListener{

//某个地方调用
singleTapCallback?.invoke(position,rv.getChildViewHolder(child))

//然后定义的回调
var singleTapCallback:((position: Int, viewHolder: RecyclerView.ViewHolder)->Unit)?=null
}

然后是正式使用的地方

        rv_temp.addOnItemTouchListener(ItemTouchListener(rv_temp).apply {
            singleTapCallback=this@FragmentTempTest::singleTab
//或者这样调用也可以
            singleTapCallback={position, viewHolder -> 
                
            }
        })

//这个就是我们的回调实现方法拉。
   fun singleTab(position: Int, viewHolder: RecyclerView.ViewHolder){

    }

这里还有一些
https://blog.csdn.net/lmo28021080/article/details/81505211

inline,noline

参考https://blog.csdn.net/Jaden_hool/article/details/78437947

基本数据类型

比较坑的地方,就是不能互相自动转化,java里我们定义个float,传个int也没问题的,在kt里就不行
有点,数字下边可以加下划线,看起来更直观

val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
== === 2个等号和3个等号的区别

前者是数值比较,后者是地址比较
== 和 ===
其中这个比较吓人

fun main(args: Array) {
   val a: Int = 1000
   val b: Int? = a
   val c: Int? = a
   println(b == c)    //true
   println(b === c)   //false    
}


fun main(args: Array) {
   val a: Int = 100
   val b: Int? = a
   val c: Int? = a
   println(b == c)    //true
   println(b === c)   //true    
}

两段代码除了a的初始值不一样,其他都一样,可结果3个等号的结果不一样。
这个就是java里基本数据类型和对象的问题了,看下源码,128以下的,用的是同一个对象,从缓存数组读出来的,所以对象比较也是相等的,128以上的,就是每次new一个新的了,不是一个对象了。

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
运算符

这是完整的位运算列表(只用于 Int 与 Long):

shl(bits) – 有符号左移 (Java 的 <<)
shr(bits) – 有符号右移 (Java 的 >>)
ushr(bits) – 无符号右移 (Java 的 >>>)
and(bits) – 位与
or(bits) – 位或
xor(bits) – 位异或
inv() – 位非

后缀 u 与 U 将字面值标记为无符号

val l: ULong = 1u  // ULong,已提供预期类型

val a1 = 42u // UInt:未提供预期类型,常量适于 UInt
"""

原始字符串 使用三个引号(""")分界符括起来,内部没有转义并且可以包含换行以及任何其他字符:
比如下边这个,最终显示结果有4行,第一个换行,最后一行换行的,中间的空白页都保留的

val text = """
    for (c in "foo")
        print(c)
"""
when

常见的when用法,类似java里的swtich,不过比这个强大很多

when (x) {
    parseInt(s) -> print("s encodes x")
   in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
is String -> x.startsWith("prefix")
    else -> print("s does not encode x")
}

when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

还有这种

fun Request.getBody() =
        when (val response = executeRequest()) {
            is Success -> response.body
            is HttpError -> throw HttpException(response.status)
        }
密封类

用sealed 修饰class
sealed

for

for的特殊用法,迭代类型的可以直接拿到key和value

        val arr= arrayListOf(2,3,3,33,3)
        for((index,value) in arr.withIndex()){
            
        }
标签lable

标示符后边加个@即可

loop@ for (i in 1..100) {
    // ……
}

函数

主构造函数和次构造函数,还有init的执行顺序

主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中。

在实例初始化期间,初始化块按照它们出现在类体中的顺序执行,与属性初始化器交织在一起:
下边4句的结果是按顺序打印的

class InitOrderDemo(name: String) {
    val firstProperty = "First property: $name".also(::println)
    
    init {
        println("First initializer block that prints ${name}")
    }
    
    val secondProperty = "Second property: ${name.length}".also(::println)
    
    init {
        println("Second initializer block that prints ${name.length}")
    }
}

如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数用 this 关键字即可:

class Person(val name: String) {
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

初始化块中的代码实际上会成为主构造函数的一部分。委托给主构造函数会作为次构造函数的第一条语句,因此所有初始化块中的代码都会在次构造函数体之前执行。即使该类没有主构造函数,这种委托仍会隐式发生,并且仍会执行初始化块:
下边init代码先执行的

class Constructors {
    init {
        println("Init block")
    }

    constructor(i: Int) {
        println("Constructor")
    }
}
lateinit 和by lazy

lateinit 是给var用的,而且必须给予值,页面销毁的时候如果没有赋值,就挂了。
by lazy是给val用的, 后边跟一个代码块,代码块最后一行就是返回值,并且代码块只会执行一次,也就是第一次调用s这个变量的时候println会打印,再后边调用s这个值,就不会执行这个代码块了,s已经有值了,直接返回即可。

        val s:String by lazy {
            println("lazy==============")
            "xxxx"
        }

        lateinit var ss:String
覆盖属性

在超类中声明然后在派生类中重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。每个声明的属性可以由具有初始化器的属性或者具有 getter 方法的属性覆盖

open class Foo {
    open val x: Int get() { …… }
}

class Bar1 : Foo() {
    override val x: Int = ……
}

你也可以用一个 var 属性覆盖一个 val 属性,但反之则不行.因为一个 val 属性本质上声明了一个 getter 方法,而将其覆盖为 var 只是在子类中额外声明一个 setter 方法。你可以在主构造函数中使用 override 关键字作为属性声明的一部分。

interface Foo {
    val count: Int
}

class Bar1(override val count: Int) : Foo

class Bar2 : Foo {
    override var count: Int = 0
}
覆盖方法

2个父类有同名的方法,子类咋调用,用尖括号声明父类

open class A {
    open fun f() { print("A") }
    fun a() { print("a") }
}

interface B {
    fun f() { print("B") } // 接口成员默认就是“open”的
    fun b() { print("b") }
}

class C() : A(), B {
    // 编译器要求覆盖 f():
    override fun f() {
        super.f() // 调用 A.f()
        super.f() // 调用 B.f()
  }
}

扩展函数

定义也比较简单,想扩展哪个类,就点一下

class D:()
fun D.foo() = "d"

扩展属性

扩展属性不能有初始化器,他们的行为只能由显式提供的 getters/setters 定义。

val  List.lastIndex: Int
    get() = size - 1

消费者 in, 生产者 out

如下方法里的参数,有个in修饰,如果用out修饰,你在方法体里边无法修改args参数的,in可以修改

override fun invoke(proxy: Any?, method: Method, args: Array): Any? {}

可变参数

java里省略号表示可变参数,可以传多个参数,也可以传个数组,可kotlin调用的时候咋办?

//java里代码如下
public native Object invoke(Object obj, Object... args)

//kotlin里我们拿到的是个数组,下边的参数args
override fun invoke(proxy: Any?, method: Method, args: Array)
//使用的时候如下,前边加个星号
method.invoke(xxx,*args);

你可能感兴趣的:(kotlin使用记录)