美文网首页
《Kotlin极简教程》笔记

《Kotlin极简教程》笔记

作者: Aisen | 来源:发表于2020-02-23 18:57 被阅读0次

    第3章 Kotlin语言基础

    3.2 声明变量和值

    在Kotlin中,一切都是对象。所以,变量也是对象 (即任何变量都是根据引用类型来使用)

    变量分为 var(可变的)和 val(不可变的)

    尽量在Kotlin中首选使用 val 不变值,好处:可预测的行为、线程安全

    3.5 流程控制语句

    3.5.2 when表达式

    正常格式

    fun cases(obj: Any) {
        when (obj) {
            1 -> print("第一项")
            "hello" -> print("这个是字符串hello")
            is Long -> print("这是一个Long类型数据")
            !is String -> print("这不是String类型的数据")
            else -> print("else类似Java中的default")
        }
    }
    

    如果我们有很多分支需要相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔

    fun switch(x: Any) {
        when (x) {
            -1, 0 -> print("x == -1 or x == 0")
            1 -> print("x == 1")
            2 -> print("x == 2")
            else -> {
               print("x is neither 1 nor 2") 
            }
        }
    }
    

    我们可以用任意表达式(而不只是常量)作为分支条件

    fun switch(x: Any) {
        val s = "123"
        when (x) {
            -1, 0 -> print("x == -1 or x == 0")
            1 -> print("x == 1")
            2 -> print("x == 2")
            parseInt(s) -> print("x is 123")
            else -> {
               print("x is neither 1 nor 2")
            }
        }
    }
    

    我们也可以坚测一个值在in或者不在!in一个区间或者集合中:

    val x = 1
    val validNumbers = arrayOf(1, 2, 3)
    when (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")
        else -> print("none of the above")
    }
    

    第4章 基本数据类型与类型系统

    4.8 类型检测与类型转换

    4.8.2 as运算符

    as运算符用于执行引用类型的显式类型转换。

    如果要转换的类型与指定的类型兼容,转换就会成功进行;

    如果类型不兼容,使用as?运算符就会返回值null

    第5章 集合类

    5.1 集合类是什么

    5.1.2 集合类是一种数据结构

    编程的本质:数据结构 + 算法(信息的逻辑结构及其基本操作)

    5.1.3 连续存储和离散存储

    连续存储:数组,操作数据时,根据离首地址的偏移量直接存取相应位置上的数据;
    但是在数组中任意位置上插入一个元素,就需要先把后面的元素集体向后移动位置,为其空出存储空间

    离散存储:链表,与上面相反

    选择:查找较多最好用数组,添加或删除比较多最好选链表。

    5.2 Kotlin集合类简介

    Kotlin的集合类分:可变集合类(Mutable)与不可变集合类(Immutable)

    集合类有3种:list、set、map

    5.3 List

    有很多扩展函数可以用一用

    5.4 Set

    5.5 Map

    第6章 泛型

    协变、逆变、in、out

    6.1 泛型(Generic Type)

    6.1.1 为什么要有类型参数

    由于我们不能笼统地把集合类中所有的对象视作Object,然后在使用的时候各自作强制类转换。

    所以,引入类型参数解决这个类型安全使用的问题。

    6.2 型变(Variance)

    6.2.1 Java的类型通配符

    Java泛型的通配符有两种形式。我们使用

    • 子类型上界限定符? extends T指定类型参数的上限(该类型必须是类型T或者它的子类型)
    • 超类型下界限定符? super T指定类型参数的下限(该类型必须是类型T或者它的父类型)

    我们称之为类型通配符(Type Wildcard)。默认的上界(如果没有声明)是Any?下界是Nothing

    示例代码

    class Animal {
        public void act(List<? extends Animal> list) {
            for (Animal animal : list) {
                animal.eat();
            }
        }
    
        public void eat() {
            System.out.println("Eating");
        }
    }
    

    示例类型的层次关系,如图

    对象层次类图:


    集合类泛型层次类图:

    List<? extends Animal>List<Animal>,List<Dog>等的父类型,对于任何的List<X>这里的X只要是Animal的子类型,那么List<? extends Animal>就是List<X>的父类型。

    使用通配符List<? extends Animal>的引用,我们不可以往这个List中添加Animal类型以及其子类型的元素。如图,Java编译器是不允许的。

    因为对于set方法,编译器无法知道具体的类型,所以会拒绝这个调用。但是,如果是get方法形式的调用,则是允许的:

    List<? extends Animal> list1 = new ArrayList<>();
    List<Dog> list4 = new ArrayList<>();
    list4.add(new Dog());
    animal.act(list4);
    list1 = list4;
    animal.act(list1);
    

    我们这里把引用变量List<? extends Animal> list1直接赋值List<Dog> list4,因为编译器知道可以把返回对象转换为一个Animal类型。

    相应的,? super T超类型限定符的变量类型List<? super ShepherDog>的层次结构如下

    如果把一个对象为声明、使用两部分的话,

    泛型:侧重于类型的声明的代码复用,用于定义内部数据类型的参数化。

    通配符:侧重于使用上的代码复用,通配符则用于定义使用的对象类型的参数化。

    6.2.2 协变(convariant)与逆变(contravariant)

    在Java中数组是协变的,下面的代码可以正确编译:

    Integer[] ints = new Integer[3];
    ints[0] = 0;
    ints[1] = 1;
    Number[] numbers = new Number[3];
    numbers = ints;
    for (Number n : numbers) {
        System.out.println(n);
    }
    

    在Java中,因为IntegerNumber的子类型,数组类型Integer[]也是Number[]的子类型。

    而另一方面,泛型不是协变的。编译器报错提示如下:


    使用通配符,任然是报错的:


    逆变与协变

    Animal类型(简记为F,Father)是Dog类型(简记为C,Child)的父类型,我们简记为F<|C
    而List,List的类型,我们记为f(F),f(C)

    当F<|C时,如果有f(F)<|f(C),那么f叫做协变(Convariant);
    当F<|C时,如果有f(C)<|f(F),那么发叫做逆变(Contravariance)

    协变和逆变都是类型安全的。

    <? extends T>实现了泛型的协变
    List<? extends Nubmer> list = new ArrayList<>()

    这里? extends Number表示是Number类或其子类,即表示类型的上界为Number,简记为C
    这里C<|Number,这个关系成立:List<C> <| List<Number>。即:

    List<? extends Nubmer> list1 = new ArrayList<Interger>()
    List<? extends Nubmer> list2 = new ArrayList<Float>()
    

    但这里不能向list1、list2添加null以外的任意对象

    list1.add(null)
    
    list1.add(new Integer(1)); //error
    list2.add(new Float(2)); //error
    

    为了保护类型一致,禁止向List<? extends Number>添加任意对象,不过可以添加null

    <? super T>实现了泛型的逆变
    List<? super Nubmer> list = new ArrayList<>()

    ? super Number通配符表示Number类或其父类,即表示的类型下界为Number。这里的父类型是? super Number,子类型C是Number。即当F<|C,有f(C)<|f(F),这就是逆变

    PECS:producer-extends,consumer-super, Get and Put Principle.

    6.3 Kotlin的泛型特色

    Kotlin引入生产者和消费者的概念,即前面讲的PECS。
    生产者是我们去读取数据的对象,消费者是我们写入数据的对象

    6.3.1 out Tin T

    Kotlin,只能保证读取数据时类型安全的对象叫做生产者,用out T标记,
    只能保证写入数据安全时类型安全的对象叫做消费者,用in T标记

    可以这么记

    out T等价于? extens T``in T等价于? super T,此外,还有*等价于?

    第7章 面向对象编程(OOP)

    7.9 单例模式(Singleton)与伴生对象(companion object)

    7.9.2 object对象

    kotlin中没有静态属性和方法,但是也提供了实现类似于单例的功能,我们可以使用关键字object声明一个object对象

    object AdminUser {
        val username: String = "admin"
        val password: String = "admin"
        fun getTimestamp() = SimpleDateFormat("yyyyMMddHHmmss").format(Date())
        fun md5Password() = EncoderByMd5(password + getTimestamp())
    }
    

    为了更加直观的了解object对象的概念,我们把上面的object User的代码反编译成Java代码:

    public final class User {
       @NotNull
       private static final String username = "admin";
       @NotNull
       private static final String password = "admin";
       public static final User INSTANCE;
    
       @NotNull
       public final String getUsername() {
          return username;
       }
    
       @NotNull
       public final String getPassword() {
          return password;
       }
    
       private User() {
          INSTANCE = (User)this;
          username = "admin";
          password = "admin";
       }
    
       static {
          new User();
       }
    }
    

    从上面的反编译代码,我们可以直观了解Kotlin的object背后的一些原理。

    7.13 委托(Delegation)

    7.13.2 类的委托(Class Delegation)

    就像支持单例模式的object对象一样,Kotlin 在语言层面原生支持委托模式。

    interface Subject {
        fun hello()
    }
    
    class RealSubject(val name: String) : Subject {
        override fun hello() {
            val now = Date()
            println("Hello, REAL $name! Now is $now")
        }
    }
    
    class ProxySubject(val sb: Subject) : Subject by sb {
        override fun hello() {
            println("Before ! Now is ${Date()}")
            sb.hello()
            println("After ! Now is ${Date()}")
        }
    }
    
    fun main(args: Array<String>) {
        val subject = RealSubject("World")
        subject.hello()
        println("-------------------------")
        val proxySubject = ProxySubject(subject)
        proxySubject.hello()
    }
    

    在这个例子中,委托代理类ProxySubject继承接口Subject,并将其所有共有的方法委托给一个指定的对象sb:

    class ProxySubject(val sb: Subject) : Subject by sb 
    

    ProxySubject的超类型Subject中的by sb表示sb将会在ProxySubject中内部存储。

    (注:对这个还不是很理解,自测了下,加不加by sb,log都一样)

    7.13.3 委托属性(Delegated Properties)

    通常对于属性类型,我们是在每次需要的时候手动声明它们:

    class NormalPropertiesDemo {
        var content: String = "NormalProperties init content"
    }
    

    那么这个content属性将会很“呆板”。属性委托赋予属性富有变化的活力。

    例如:

    • 延迟属性(lazy properties): 其值只在首次访问时计算
    • 可观察属性(observable properties): 监听器会收到有关此属性变更的通知
    • 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。

    第8章 函数式编程(FP)

    第9章 轻量级线程:协程

    9.1 协程简介

    协程提供了一种避免阻塞线程并用更简单、更可控的操作替代线程阻塞的方法:协程挂起。

    协程主要是让原来要使用“异步+回调方式”写出来的复杂代码, 简化成可以用看似同步的方式写出来(对线程的操作进一步抽象)。

    9.13 协程与线程比较

    区别:协程是编译器级的,而线程是操作系统级的。

    协程是用户空间下的线程。

    线程是抢占式的,而协程是非抢占式的。

    线程是协程的资源。

    9.14 协程的好处

    协程依靠user-space调度,而线程、进程则是依靠kernel来进行调度。
    线程、进程间切换都需要从用户态进入内核态,而协程的切换完全是在用户态完成,且不像线程进行抢占式调度,协程是非抢占式的调度。

    协程的程序只在用户空间内切换上下文,不再陷入内核来做线程切换,这样可以避免大量的用户空间和内核空间之间的数据拷贝,降低了CPU的消耗。

    使用协程,我们不再需要像异步编程时写那么一堆callback函数,代码结构不再支离破碎,整个代码逻辑上看上去和同步代码没什么区别,简单,易理解,优雅。

    9.15 协程的内部机制

    9.15.1 基本原理

    协程完全通过编译技术实现(不需要来自 VM 或 OS 端的支持),挂起机制是通过状态机来实现,其中的状态对应于挂起调用。

    第10章 Kotlin与Java操作

    Java调用Kotlin

    Java可以调用Kotlin代码,但是要多用一些注解语法。

    @JvmName 注解修改生成的Java类的类名 (不建议修改,推荐Kotlin默认的命名生成规则)

    @JvmField 注解标注Kotlin中的属性字段,表示这个一个实例字段,不会生成getters/setter方法

    @JvmStatic 注解静态方法,在相应的类中生成静态方法

    这些注解语法是编译器为了更加方便Java调用Kotlin代码提供的一些技巧,使在Java中调用Kotlin代码更加自然优雅些。

    JvmOverloads注解,生成额外的重载函数给Java调用

    Throws(Exception::class),让Kotlin的异常变成受检的,让Java编译器可以检查到。(在Kotlin中,所有异常都是非受检的,在运行时,这个异常还是抛出来的)

    Kotlin与Java对比

    打印日志

    • Java
    System.out.print("Java");
    System.out.println("Java");
    
    • Kotlin
    print("Kotlin")
    println("Kotlin")
    

    其实,Kotlin中的println函数是一个内联函数,它其实就是通过封装java.lang.System类的System.out.println来实现的。

    @kotlin.internal.InlineOnly
    public inline fun print(message: Any?) {
        System.out.print(message)
    }
    

    常量与变量

    • Java
    String name = "KotlinVSJava";
    final String name = "KotlinVSJava";
    
    • Kotlin
    var name = "KotlinVSJava"
    val name = "KotlinVSJava"
    

    null声明

    • Java
    String otherName;
    otherName = null;
    
    • Kotlin
    var otherName : String?
    otherName = null
    

    空判断

    • Java
    if (text != null) {
        int length = text.length();
    }
    
    • Kotlin
    text?.let {
        val length = text.length
    }
    // 或者
    val length = text?.length
    

    在Kotlin中,我们只使用一个问号安全调用符号就省去了Java中烦人的if - null 判断。

    字符串拼接

    • Java
    String firstName = "Jack";
    String lastName = "Chen";
    String message = "My name is: " + firstName + " " + lastName;
    
    • Kotlin
    val firstName = "Jack"
    val lastName = "Chen"
    val message = "My name is: $firstName $lastName"
    

    Kotlin中使用$${}(花括号里面是表达式的时候)占位符来实现字符串的拼接,这个比在Java中每次使用加号来拼接要方便许多。

    换行

    • Java
    String text = "First Line\n" +
                  "Second Line\n" +
                  "Third Line";
    
    • Kotlin
    val text = """
            |First Line
            |Second Line
            |Third Line
            """.trimMargin()
    

    三元表达式

    • Java
    String text = x > 5 ? "x > 5" : "x <= 5";
    
    • Kotlin
    val text = if (x > 5)
                  "x > 5"
               else "x <= 5"
    

    操作符

    • java
    final int andResult  = a & b;
    final int orResult   = a | b;
    final int xorResult  = a ^ b;
    final int rightShift = a >> 2;
    final int leftShift  = a << 2;
    
    • Kotlin
    val andResult  = a and b
    val orResult   = a or b
    val xorResult  = a xor b
    val rightShift = a shr 2
    val leftShift  = a shl 2
    

    类型判断和转换(显式)

    • Java
    if (object instanceof Car) {
    }
    Car car = (Car) object;
    
    • Kotlin
    if (object is Car) {
    }
    var car = object as Car
    

    类型判断和转换 (隐式)

    • Java
    if (object instanceof Car) {
       Car car = (Car) object;
    }
    
    • Kotlin
    if (object is Car) {
       var car = object // Kotlin智能转换
    }
    

    Kotlin的类型系统具备一定的类型推断能力,这样也省去了不少在Java中类型转换的样板式代码。

    Range区间

    • Java
    if (score >= 0 && score <= 300) { }
    
    • Kotlin
    if (score in 0..300) { }
    

    更灵活的case语句

    • Java
        public String getGrade(int score) {
            String grade;
            switch (score) {
                case 10:
                case 9:
                    grade = "A";
                    break;
                case 8:
                case 7:
                case 6:
                    grade = "B";
                    break;
                case 5:
                case 4:
                    grade = "C";
                    break;
                case 3:
                case 2:
                case 1:
                    grade = "D";
                    break;
                default:
                    grade = "E";
            }
            return grade;
        }
    
    • Kotlin
    fun getGrade(score: Int): String {
        var grade = when (score) {
            9, 10 -> "A"
            in 6..8 -> "B"
            4, 5 -> "C"
            in 1..3 -> "D"
            else -> "E"
        }
        return grade
    }
    

    for循环

    • Java
    for (int i = 1; i <= 10 ; i++) { }
    for (int i = 1; i < 10 ; i++) { }
    for (int i = 10; i >= 0 ; i--) { }
    for (int i = 1; i <= 10 ; i+=2) { }
    for (int i = 10; i >= 0 ; i-=2) { }
    for (String item : collection) { }
    for (Map.Entry<String, String> entry: map.entrySet()) { }
    
    • Kotlin
    for (i in 1..10) { }
    for (i in 1 until 10) { }
    for (i in 10 downTo 0) { }
    for (i in 1..10 step 2) { }
    for (i in 10 downTo 1 step 2) { }
    for (item in collection) { }
    for ((key, value) in map) { }
    

    更方便的集合操作

    • Java
    final List<Integer> listOfNumber = Arrays.asList(1, 2, 3, 4);
    final Map<Integer, String> map = new HashMap<Integer, String>();
    map.put(1, "Jack");
    map.put(2, "Ali");
    map.put(3, "Mindorks");
    
    • Kotlin
    val listOfNumber = listOf(1, 2, 3, 4)
    val map = mapOf(1 to "Jack", 2 to "Ali", 3 to "Mindorks")
    

    遍历

    • Java
    // Java 7 
    for (Car car : cars) {
      System.out.println(car.speed);
    }
    // Java 8+
    cars.forEach(car -> System.out.println(car.speed));
    // Java 7 
    for (Car car : cars) {
      if (car.speed > 100) {
        System.out.println(car.speed);
      }
    }
    // Java 8+
    cars.stream().filter(car -> car.speed > 100).forEach(car -> System.out.println(car.speed));
    
    • Kotlin
    cars.forEach {
        println(it.speed)
    }
    cars.filter { it.speed > 100 }
          .forEach { println(it.speed)}
    

    方法(函数)定义

    • Java
    void doSomething() {
       // 实现
    }
    void doSomething(int... numbers) {
       // 实现
    }
    
    • Kotlin
    fun doSomething() {
       // 实现
    }
    fun doSomething(vararg numbers: Int) {
       // 实现
    }
    

    带返回值的方法(函数)

    • Java
    int getScore() {
       // logic here
       return score;
    }
    
    • Kotlin
    fun getScore(): Int {
       // logic here
       return score
    }
    // 单表达式函数
    fun getScore(): Int = score
    

    另外,Kotlin中的函数是可以直接传入函数参数,同时可以返回一个函数类型的。

    constructor 构造器

    • Java
    public class Utils {
        private Utils() { 
          // 外部无法来调用实例化
        }
        
        public static int getScore(int value) {
            return 2 * value;
        }
        
    }
    
    • Kotlin
    class Utils private constructor() {
        companion object {
        
            fun getScore(value: Int): Int {
                return 2 * value
            }
            
        }
    }
    // 或者直接声明一个object对象
    object Utils {
        fun getScore(value: Int): Int {
            return 2 * value
        }
    }
    

    JavaBean与Kotlin数据类

    这段Kotlin中的数据类的代码:

    data class Developer(val name: String, val age: Int)
    

    对应下面这段Java实体类的代码:

    • Java
    public final class Developer {
       @NotNull
       private final String name;
       private final int age;
       @NotNull
       public final String getName() {
          return this.name;
       }
       public final int getAge() {
          return this.age;
       }
       public Developer(@NotNull String name, int age) {
          Intrinsics.checkParameterIsNotNull(name, "name");
          super();
          this.name = name;
          this.age = age;
       }
       @NotNull
       public final String component1() {
          return this.name;
       }
       public final int component2() {
          return this.age;
       }
       @NotNull
       public final Developer copy(@NotNull String name, int age) {
          Intrinsics.checkParameterIsNotNull(name, "name");
          return new Developer(name, age);
       }
       // $FF: synthetic method
       // $FF: bridge method
       @NotNull
       public static Developer copy$default(Developer var0, String var1, int var2, int var3, Object var4) {
          if((var3 & 1) != 0) {
             var1 = var0.name;
          }
          if((var3 & 2) != 0) {
             var2 = var0.age;
          }
          return var0.copy(var1, var2);
       }
       public String toString() {
          return "Developer(name=" + this.name + ", age=" + this.age + ")";
       }
       public int hashCode() {
          return (this.name != null?this.name.hashCode():0) * 31 + this.age;
       }
       public boolean equals(Object var1) {
          if(this != var1) {
             if(var1 instanceof Developer) {
                Developer var2 = (Developer)var1;
                if(Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age) {
                   return true;
                }
             }
             return false;
          } else {
             return true;
          }
       }
    }
    

    相关文章

      网友评论

          本文标题:《Kotlin极简教程》笔记

          本文链接:https://www.haomeiwen.com/subject/sbrsqhtx.html