乐趣区

关于android:Kotlin-学习笔记一

最近开始学习 Kotlin 语言了,打算搞个笔记系列,这是首篇~

根本类型

    var age: Int = 123    // 规范语法,申明一个可变变量 age
    val name: String = "Tom"    // 规范语法,申明一个不可变变量 name (不可变变量不是常量)
    val sex: String? = null    // 申明一个可为空的字符串变量 sex,String 与 String? 不是同种类型

String 和 String? 是两种类型,前者润饰的变量不可为 null 空值;后者加了“?”之后润饰的变量就能够为 null 了,这也是 kotlin 空平安的一种体现。

当编译器能够推断出变量的数据类型时,能够不必写冒号和前面的数据类型,例如:

    var age = 123    // 可推断出 age 为 Int,所以可不写
    val name = "Tom"    // 可推断出 name 为 String,所以可不写

双感叹号“!!”能够强转类型,如下代码。name2 是可为空的 String? 类型,间接赋给不可为空的 name1 就会报错。如果确定 name2 肯定不为空,则能够在前面加上 “!!” 强转。

    var name1: String = "Tom"    // name1 不可为空
    var name2: String? = "Jack";    // name2 可为空
    //name1 = name2    // 报错
    name1 = name2!!

关键字

  1. open。被申明为 open 的 class 是能够被继承的,这里留神下 kotlin 中一个类是默认被润饰为 final 的,即默认的类是不能被继承的。

函数

kotlin 函数前有 fun 关键字,返回值类型要写在入参括号后和函数体大括号前:

fun main() {printLen("栗子")
}

fun printLen(str: String): String {println("举个 $str !")    // 这种写法相似于 C 语言了
    return str
}

// Kotlin 函数参数还能够设置默认值
fun printLen(str: String = "我是默认值~"): String {println("举个 $str !")    // 这种写法相似于 C 语言了
    return str
}

Kotlin 办法能够间接写在 .kt 文件里,不必写在某个类中

例如有个 Util.kt 的文件,外面有许多工具类的办法,如果在 Java 中,就必须在类中编写代码:

public class Utils {public static final void echo(String name) {println("name =" + name);
    }
}

调用时,就是:

Utils.echo("Hello UnderWorld!");

而在 Kotlin 代码中,能够间接在 Util.kt 文件中这么写:

// Util.kt 文件
fun echo(name: String) {println("name = $name")
}

在 Java 代码里调用就能够间接这么写:

// Main.java 文件
public static void main(String[] args) {UtilKt.echo("Hello World!");
}

与 Java 代码之间的互调

object Test {    // Kotlin 代码里 匿名外部类 的写法
    fun say(msg: String) {println(msg)
    }
}

在 kotlin 代码中调用 Test 中的 say 办法:

Test.say("Good Morning~")

在 Java 代码中调用,则:

Test.INSTANCE.say("Good Morning~")

在 kotlin 中调用一个 Java 类,不能像在 Java 中一样写成这样:Test.class,而是要这样写:Test::class.java。另外 Kotlin 类是被编译为 KClass 文件,而不是 class 文件。所以,在 Kotlin 代码里,如果要调用一个 Kotlin 的类,则不必加 .java 后缀,而是间接写成:Util::class

Java 与 Kotlin 之间的抵触解决

  1. 关键字抵触。比方 in 这个关键字,在 Kotlin 中是一个关键字,如果要援用 Java 类中一个叫 in 的对象时,则须要用反引号 ` 解决这个抵触:

    Utils.`in`   // 在 Utils.java 中,in 是一个属性:public static int in = 100;
  2. Kotlin 没有封装类。Kotlin 中没有像 Integer 的封装类,只有 Int 等根本类型,只有通过反射的形式能力调用或用于甄别 Integer 的封装类类型。
    这里给出几个网上利用的例子,理论中应用时,再补充。
    1)在 kotlin 代码中应用 Integer.class。如果 Java 类中有办法:void func(Class clazz){},那么在 Kotlin 中如果须要传入一个 Integer.class 该怎么办?正确的做法是:func(Int::class.javaObjectType),而不是 func(Int::class.java)
    2)Int::class.java 指向的是 kotlin 规范库中的 Int.kt;Int::class.javaObjectType指向的是 JDK 里的 Integer.java 类。
  3. Kotlin 是空平安的。Kotlin 如果调用了 Java 中的代码,则须要用 *? 的类型来接管,这样能够避免空指针异样。例如 Java 中是 String 类型的对象,要在 Kotlin 中应用的话,须要用 String? 类型来接管。
  4. Kotlin 没有动态变量和静态方法。没有静态方法的问题,能够在办法前增加 @JvmStatic 注解来解决:

    object Utils {
     @JvmStatic
     fun getName(): String{return "hehe"}
    }

    当然也能够将办法写在类的 companion object {}中。

扩大函数

kotlin 反对给原有的类增加一些扩大的性能,就是通过扩大函数来实现的。能够针对第三方库中对象增加一些咱们须要的办法。例如咱们能够扩大一下 User 类中的办法:

fun User.getInfo(): String {    // 本来的 User 类中是没有 getInfo 办法的
    return uid.toString() + name}

这样,咱们相当于给 User 类增加了一个办法 getInfo,而后 User 类的对象都能够调用 getInfo 办法了。请留神这里的扩大函数是动态增加给这个类的,不具备运行时的多态的。能够看上面的代码:

open class Animal    // 父类
class Dog: Animal()    // 子类

fun Animal.name() = "animal"    // 父类扩大函数 name,返回 animal 
fun Dog.name() = "dog"    // 子类扩大函数 name,返回 dog

fun Animal.printName(animal: Animal) {    // 父类扩大函数 printName,调用的是父类对象的 name 函数
    println(animal.name())
}

fun main(args: Array<String>) {Dog().printName(Dog())    // 打印的后果是“animal”,这阐明扩大函数不具备运行时多态的特点。}

将这段代码反编译成 Java 代码,能够看到最终调用的 Dog().printName(Dog())这段代码,被编译成了 printName((Animal)(new Dog()), (Animal)(new Dog()));,即最初调用会将 Dog 对象强转为 Animal 对象,这样就不具备多态的特点了。

Lambda 闭包

  1. Lambda 闭包申明,能够为:

    // lambda 闭包
    val print = {name: String ->    // 闭包名申明为 print,闭包还容许增加参数,这里申明了一个 name 的参数
     println(name)
    }

    这里闭包中的参数个数是有限度的,下限为 22 个。因为 Kotlin 只为咱们定义了含有 22 个参数的 Function22,如图所示:

如果咱们须要用到 23 个参数的 Lambda 闭包该怎么办呢?这个时候咱们就须要手动申明一个 kotlin 包中的 Function23。这里须要手动定义一个 Java 类的 Function23,因为只有一个 kotlin 规范库才能够申明一个 kotlin 包名,而咱们本人是不能申明一个类的包名为 kotlin 的,然而 Java 和 kotlin 是互通的,所以咱们能够将这个 Function23 申明为一个 Java 类,并将它的包名设置为 kotlin,这样就能够申明参数个数超过 22 的闭包了。

package kotlin;

public interface Function23<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, R> extends Function<R> {R invoke(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10, P11 p11, P12 p12, P13 p13, P14 p14, P15 p15, P16 p16, P17 p17, P18 p18, P19 p19, P20 p20, P21 p21, P22 p22, P23 p23);
}

高阶函数

高阶函数的特点:函数(Lambda)的参数也是一个函数(Lambda)。
知识点 1:函数默认的返回值为一个 Unit 类型的对象,能够不写。但如果这个函数是作为一个参数,那么返回类型肯定要写:

// 只有当 isDebug 为 true,才会执行前面的 block 函数.  block 函数是作为一个参数,所以返回类型要显式写出
fun Onlyif(isDebug: Boolean, block: () -> Unit) {if (isDebug) block()}

fun main() {
    val runnable = Runnable {print("Runnable run!")
    }
    val function: () -> Unit
    function = runnable::run    // Runnable 只有一个 run 办法,所以能够间接用双冒号进行调用
    Onlyif(true, function)
}

Kotlin 的 Lambda 会编译为一个匿名外部类,能够应用 inline 关键字来润饰办法,这样当办法在编译时就会拆解办法的调用为语句调用,进而缩小创立不必要的对象。
但要留神,过多应用 inline 关键字会减少编译器的编译累赘,所以 inline 只适宜润饰高阶函数,例如上述的高阶函数就能够用 inline 润饰:

inline fun Onlyif(isDebug: Boolean, block: () -> Unit) {if (isDebug) block()}

类与对象

  1. Kotlin 类默认是被 public final 润饰的,默认的父类是 Any,而不是 Object。
  2. Kotlin 类的构造函数会默认调用 init 办法,所以能够在 init 办法中执行一些初始化的操作:

    class TestView: View {constructor(context: Context): super(context)
    
     constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    
     constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
     
     init {print("构造函数已执行~")
     }
    }
  3. 四种拜访修饰符,别离是 private、protected、public、internal。前三种与 Java 雷同,internal 示意 module 模块外部是都能够拜访的,而其余 module 是无法访问的。
  4. Kotlin 的伴生对象。能够实现静态方法和动态变量:

    class StringUtils {
     // 伴生对象
     companion object {
         // 伴生对象实现动态变量
         val TAG = "StringUitls"
         // 伴生对象实现静态方法
         fun isEmpty(str: String) : Boolean {return "" == str}
     }
    }
  5. Kotlin 中的单例的实现。能够应用伴生对象来实现 kotlin 的单例:

    // 单例实现
    class SingleInstance private constructor() {
     companion object {fun get() : SingleInstance {return Holder.instance}
     }
     
     private object Holder {    // 通过 object 创立一个匿名外部类
         val instance = SingleInstance()}
    }

参考文献

  • 从 Kotlin 拜访 Integer.class
  • Kotlin 调用 Java 写的办法,参数 Class<T> 神坑

更多内容,欢送关注公众号:修之竹
或者查看 修之竹的 Android 专辑

也欢送知乎搜寻 修之竹~

ps. 赠人玫瑰,手留余香。欢送转发分享关注,你的认可是我持续创作的精力源泉。

退出移动版