Bootstrap

【Android】Kotlin教程(7)

总结:Kotin这门语言是谁设计的,语法很辣鸡,纯属是为了造门语言而造语言。

1.函数式编程

Kotlin 是一种现代的多范式编程语言,支持面向对象编程(OOP)和函数式编程(FP)。函数式编程是一种编程范式,它强调使用纯函数、不可变数据和高阶函数来构建程序。Kotlin 提供了许多特性来支持函数式编程风格,这些特性使得代码更加简洁、易读和易于测试。

2.变换函数map

map变换函数会遍历接受者集合,让变换器函数作用于集合里的各个元素,返回结果是包含已修改元素的集合,会作为链上下一个函数的输入。

map变换函数和定义的变换器函数做完事情后,返回的是一个新集合,原始集合没有被修改。

fun main() {
    val animals = listOf("zebra", "giraffe", "elephant", "rat")
    val babies = animals.map { animal -> "A baby $animal is so cute" }
    println(babies)
    // [A baby zebra is so cute, A baby giraffe is so cute, A baby elephant is so cute, A baby rat is so cute]
}

可以将多个 map 操作链接在一起,以实现更复杂的转换。

val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers.map { it * it }  // 计算平方
    .map { "Square of $it" }  // 转换为描述性字符串
println(result)  // 输出: [Square of 1, Square of 4, Square of 9, Square of 16, Square of 25]

3.flatMap

flatMap函数操作一个集合的集合,将其中多个集合中的元素合并后返回一个包含所有元素的单一集合。

    val result = listOf(listOf(1, 2, 3), listOf(4, 5, 6), listOf(7, 8, 9))
    val flatMap = result.flatMap { it }
    println(flatMap)
    // [1, 2, 3, 4, 5, 6, 7, 8, 9]

4.过滤函数filter

filter 函数是一个非常有用的高阶函数,用于从集合中筛选出满足特定条件的元素。它返回一个新的集合,其中只包含那些通过给定谓词(predicate)测试的元素。filter 函数是函数式编程中的一个基本工具,可以帮助你以一种简洁和声明式的方式处理数据。

fun main() {
    val list = listOf(1,2,3,4,5,6,7,8,9,10)
    val result = list.filter { it % 2 == 0 }
    println(result) // [2, 4, 6, 8, 10]
}
fun isEven(number: Int): Boolean {
    return number % 2 == 0
}

val numbers = listOf(1, 2, 3, 4, 5, 6)
val evenNumbers = numbers.filter(::isEven)
println(evenNumbers)  // 输出: [2, 4, 6]

5.合并函数zip

zip 函数用于将两个集合(如列表)中的元素配对,生成一个新的列表。每个新列表的元素是一个包含两个原集合对应位置元素的 Pair。如果两个集合的长度不同,那么结果列表的长度将与较短的那个集合相同。

fun main() {
    val numbers1 = listOf(1, 2, 3)
    val numbers2 = listOf(4, 5, 6)

    val zipped1 = numbers1.zip(numbers2)
    println(zipped1)  // 输出: [(1, 4), (2, 5), (3, 6)]


    val names = listOf("Alice", "Bob", "Charlie")
    val ages = listOf(30, 25, 35)

    val zipped2 = names.zip(ages)
    println(zipped2)  // 输出: [(Alice, 30), (Bob, 25), (Charlie, 35)]
}

6.合并函数fold

fold 函数是一个非常强大的高阶函数,用于对集合中的元素进行累积操作。它从一个初始值开始,依次应用一个函数到每个元素上,并将结果累积起来。fold 是一种减少(reduce)操作,常用于计算总和、乘积、最大值、最小值等。

fun main() {
    val numbers1 = listOf(1, 2, 3, 4, 5)
    val sum1 = numbers1.fold(0) { acc, i -> acc + i }
    println(sum1)  // 输出: 15


    val numbers2 = listOf(1, 2, 3, 4, 5)
    val product = numbers2.fold(1) { acc, i -> acc * i }
    println(product)  // 输出: 120
}

7.序列

在Kotlin中,序列(Sequence)是一个强大的工具,用于处理大数据集或进行复杂的数据转换。

定义:序列是Kotlin标准库中的一个接口,表示一个元素序列。与普通的集合不同,序列的操作是惰性的,即元素不会在创建序列时立即被处理,而是会在序列被遍历或消费时才按需生成。这种延迟执行的方式允许我们编写出更高效的数据处理逻辑。

序列的操作:

  • 中间操作:序列支持多种中间操作,如过滤(filter)、映射(map)、扁平化(flatMap)等。这些操作都是惰性的,只有在序列被消费(如通过toList()、forEach等方法)时,这些操作才会被执行。
  • 末端操作:序列的末端操作会执行原来中间操作的所有延迟计算,并返回一个结果,如集合、数字或其他对象。
// 扩展函数 Int.isPrime(),用于判断一个整数是否是质数
fun Int.isPrime(): Boolean {
    // 遍历从 2 到当前整数减 1 的所有整数
    (2 until this).forEach {
        // 如果当前整数能被任何一个遍历到的整数整除,则返回 false,表示不是质数
        if (this % it == 0) {
            return false
        }
    }
    // 如果没有找到任何能整除当前整数的数,则返回 true,表示是质数
    return true
}


fun main() {
    // 1-5000之内 可以找到1000个素数
    val toList = (1..5000).toList().filter { it.isPrime() }.take(1000)
    println(toList.size) // 670


    val sequence = generateSequence(2) { value -> value + 1 }.filter { it.isPrime() }.take(1000)
    println(sequence.toList().size) // 1000
}

通过使用 Sequence,你可以高效地处理大量数据或无限序列,而不需要一次性加载所有数据到内存中。这对于性能敏感的应用程序非常有用。

8.互操作性与可空性

互操作性是指不同系统、软件或编程语言之间能够无缝协作的能力。对于 Kotlin 来说,这意味着 Kotlin 代码可以轻松地与用其他语言(如 Java)编写的代码一起工作。

public class Jhava {
    
    @NotNull
    public String getHello() {
        return "Hello World";
    }


    @Nullable
    public String determineFriendShipLevel(){
        return null;
    }
}

fun main() {
    val adversary = Jhava()
    println(adversary.hello)
    val level = adversary.determineFriendShipLevel()
    println(level?.toLowerCase())
}

9.类型映射

代码运行时,所有的映射类型都会重新映射回对应的Java类型。

    println(adversary.hitPoints.javaClass) // int

10.属性访问

不需要调用相关的setter方法,可以使用赋值语句来设置一个Java字段值。

11.@JvmName

@JvmName 是 Kotlin 提供的一个注解,用于在生成的 Java 字节码中指定方法或属性的具体名称。这在你需要控制 Kotlin 代码生成的 Java 方法名时非常有用,特别是在与 Java 代码互操作的情况下。

    public static void main(String[] args) {
        System.out.println(Hero.makeProclamation());
    }
@file:JvmName("Hero")

fun main() {
    val adversary = Jhava()
    println(adversary.hello)
    val level = adversary.determineFriendShipLevel()
    println(level?.toLowerCase())

    println(adversary.hitPoints.javaClass) // int
}

fun makeProclamation() = "Greeting,beast!"

12.@JvmField

@JvmField 是 Kotlin 提供的一个注解,用于在生成的 Java 字节码中直接暴露一个属性为字段(field),而不是通过 getter 和 setter 方法。这在你需要与 Java 代码进行互操作时特别有用,因为它允许 Java 代码直接访问 Kotlin 属性,就像访问普通字段一样。

class Spellbook {
    @JvmField
    val spells = listOf("Magic Ms. L","Lay on hans")
}
    public static void main(String[] args) {
        Spellbook spellbook = new Spellbook();
        for (String spell : spellbook.spells){
            System.out.println(spell);
        }
    }

13.@JvmOverloads

@JvmOverloads 是 Kotlin 提供的一个注解,用于生成具有默认参数值的方法的多个重载版本。在 Java 中,方法不支持默认参数,因此 Kotlin 会为每个可能的参数组合生成一个单独的方法。这使得 Kotlin 的默认参数功能可以与 Java 代码无缝集成。

@JvmOverloads
fun handOverFood(leftHand : String = "berrriea", rightHand : String = "beef"){
    println("$leftHand and $rightHand")
}

14.@JvmStatic

1.单例模式:
当你实现单例模式时,可以使用 @JvmStatic 来提供一个静态的方法来获取单例实例。

object Singleton {
    @JvmStatic
    fun getInstance(): Singleton = this
}

// Java 代码
public class Main {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        // 使用 singleton
    }
}

2.工具类
在工具类中,你可以使用 @JvmStatic 来定义静态方法,这样 Java 代码可以直接调用这些方法而不需要创建类的实例。

object StringUtils {
    @JvmStatic
    fun isNullOrEmpty(str: String?): Boolean {
        return str == null || str.isEmpty()
    }

    @JvmStatic
    fun reverse(str: String): String {
        return str.reversed()
    }
}


// Java 代码
public class Main {
    public static void main(String[] args) {
        boolean isEmpty = StringUtils.isNullOrEmpty("test");
        String reversed = StringUtils.reverse("hello");
        // 使用结果
    }
}

3.伴生对象
在 Kotlin 中,伴生对象(companion object)中的成员默认不是静态的。使用 @JvmStatic 可以使这些成员在 Java 中表现为静态成员。

class Spellbook {

    @JvmField
    val spells = listOf("Magic Ms. L","Lay on hans")


    companion object {
        @JvmStatic
        val MAX_SPELL_COUNT = 10
        fun getSpellbookGreeting() = println("I am the Great Grimoire!")
    }
}
System.out.println(Spellbook.getMAX_SPELL_COUNT());
Spellbook.Companion.getSpellbookGreeting();

4.枚举类

enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF);

    companion object {
        @JvmStatic
        fun fromInt(rgb: Int): Color? {
            for (color in values()) {
                if (color.rgb == rgb) {
                    return color
                }
            }
            return null
        }
    }
}
// Java 代码
public class Main {
    public static void main(String[] args) {
        Color color = Color.fromInt(0xFF0000);
        // 使用 color
    }
}

13.@Throws

@Throws 是 Kotlin 中的一个注解,用于声明一个函数可能会抛出哪些受检异常(checked exceptions)。在 Java 中,受检异常必须在方法签名中声明,而在 Kotlin 中,由于语言设计的原因,默认情况下不需要声明受检异常。然而,在某些情况下,特别是当你的 Kotlin 代码需要与 Java 代码互操作时,使用 @Throws 注解可以明确地指出一个函数可能抛出的异常类型。

import java.io.File
import java.io.IOException

fun readFile(filePath: String): String {
    val file = File(filePath)
    return file.readText()
}

在这个例子中,readText() 方法可能会抛出 IOException。为了明确这一点,你可以使用 @Throws 注解:

import java.io.File
import java.io.IOException

@Throws(IOException::class)
fun readFile(filePath: String): String {
    val file = File(filePath)
    return file.readText()
}

如果你希望这个函数能够在 Java 代码中被调用,并且 Java 代码能够捕获 IOException,那么使用 @Throws 是必要的:

public class Main {
    public static void main(String[] args) {
        try {
            String content = ExampleKt.readFile("path/to/file.txt");
            System.out.println(content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意:

  • @Throws 注解只适用于受检异常(checked exceptions)。对于运行时异常(unchecked exceptions),如 NullPointerException 或 IllegalArgumentException,不需要使用 @Throws 注解。
  • 如果一个函数可能会抛出多种类型的受检异常,可以在 @Throws 注解中列出所有可能的异常类型
@Throws(IOException::class, FileNotFoundException::class)
fun readFile(filePath: String): String {
    // 实现
}
;