新手使用 Kotlin 常碰到的问题

一、Kotlin 中的基本数据类型

(一) Int、Float、Double 等

在 Kotlin 中,Java 数据类型的封装类也是有的。与 Java 类似,Kotlin 提供了一组基本数据类型的封装类,例如 IntegerFloatDouble 等。

不同的是,Kotlin 对于封装类的使用更为简洁。在 Kotlin 中,您可以直接使用基本数据类型来进行运算和赋值,编译器会自动将其转换为对应的封装类。例如,以下代码:

ini 复制代码
val a: Int = 1
val b: Int = 2
val c: Int = a + b

在编译时会被转换为以下代码:

ini 复制代码
val a: Int = 1
val b: Int = 2
val c: Int = Integer.valueOf(a.intValue() + b.intValue()).intValue()

通过这种方式,Kotlin 程序员可以使用更为简洁的代码来操作基本数据类型和其封装类。

(二) Kotlin 中如何去调用 Java 的 Integer 对象

那么我们有没有办法去在 Kotlin 中去调用 Java 的 Integer 对象呢?办法是有的,Kotlin 可以通过反射调用 Java 的 Integer 类。

以下是一个示例代码,展示了如何使用 Kotlin 的反射 API 调用 Java 的 Integer 类的 valueOf 方法:

kotlin 复制代码
import java.lang.reflect.Method
​
fun main() {
    val method: Method = Integer::class.java.getMethod("valueOf", String::class.java)
    val integer: Any? = method.invoke(null, "123")
    println(integer) // 123
    println(integer!!::class) // class java.lang.Integer (Kotlin reflection is not available)
    val intValue: Int? = integer as? Int
    if (intValue != null) {
        println("intValue is $intValue")   // intValue is 123
    } else {
        println("intValue is null")
    }
}

在上面的示例中,我们首先获取了 Java 中的 Integer 类的 valueOf 方法的引用,然后使用 Method.invoke 方法调用该方法,并将其返回值赋值给 integer 变量。

需要注意的是,在使用 Method.invoke 方法时,由于 valueOf 方法是一个静态方法,所以我们需要将其第一个参数设为 null。此外,由于 Method.invoke 方法返回的是一个 Any 类型的对象。在 Kotlin 中,如果要将 Any 类型的值转换为 Int,可以使用类型转换符 as?。具体而言,可以将 Any 类型的值强制转换为 Int 类型,如果转换失败,则返回 null。如果确定 Any 类型的值可以转换为 Int,也可以使用非空类型转换符 as

以上就是在 Kotlin 中通过反射调用 Java 的 Integer 类的示例。

二、Kotlin 空值敏感

Kotlin 是一门空值敏感的编程语言,这意味着它更加强调对 null 的处理和检查。在 Kotlin 中,null 不被视为默认值或有效值,而是视为一种特殊的状态,需要单独处理。

为了处理可能为空的值,Kotlin 引入了两种类型:可空类型和非空类型。可空类型使用 ? 来标记,例如 String? 表示该变量可以存储 String 类型的值或 null。非空类型则没有标记,例如 String 表示该变量必须始终包含 String 类型的值,不能为 null。

在 Kotlin 中,当您尝试在一个可空类型的变量上调用一个非空的方法或属性时,编译器会在编译时引发一个空指针异常(NullPointerException)。这种方式可以帮助开发者在编写代码时更好地处理 null 值,从而避免在运行时发生空指针异常。

Kotlin 还提供了许多用于处理 null 的语言特性,例如安全调用操作符 ?.、Elvis 操作符 ?:、非空断言操作符 !!、let 函数等等,这些都可以更加方便地处理 null 值。

我们从下面的例子中分析一下 Kotlin 的可空类型和非空类型,如下所示:

Java 中定义format方法:

typescript 复制代码
public class Test {
    public static String format(String str) {
        return str.isEmpty() ? null : str;
    }
}

Kotlin 中使用三种方式,调用 format 方法:

kotlin 复制代码
fun main(args: Array<String>) {
    function("")
}
​
fun function(str: String) {
    // 第一种方式
    var fm1 = format(str)
    println(fm1.length)
    // 第二种方式
    var fm2: String = format(str)
    println(fm2.length)
    // 第三种方式
    var fm3: String? = format(str)
    println(fm3?.length)
}

接下来我们逐个分析每个调用方式。

(一)第一种方式

scss 复制代码
var fm1 = format(str)
println(fm1.length)

这里使用的是 var 关键字来定义变量 fm1,它的类型由编译器自动推断。

如果 format(str) 函数返回一个非空的字符串,则 fm1 变量类型将自动推断为 String 类型。但是,如果 format(str) 函数返回 null 值,则变量 fm1 类型将推断为 String! 类型。 String! 类型是非空类型,表示此变量可以为 null 或非 null。但是 Kotlin 编译器无法确定它是否为 null,需要开发者在使用时自行负责判空。

在这个例子中,format() 函数返回的是 null,因此需要使用可空类型的变量来接收返回值,但是没有进行非空判断就直接调用了 length 属性,因此会抛出空指针异常。

(二)第二种方式

css 复制代码
var fm2: String = format(str)
println(fm2.length)

在这行代码中,我们将 format(str) 的返回值显式地声明为 String 类型并将其赋值给 fm2 变量。因为 fm2 是一个非空类型的字符串变量,所以 Kotlin 编译器会在编译时检查 format(str) 的返回值是否为 null,如果是,就会抛出空指针异常。因此,这行代码保证了 fm2 变量不会被赋值为 null

在这个例子中,format() 函数返回的是 null,因此需要使用可空类型的变量来接收返回值,因此 var fm2: String = format(str) 就会抛出空指针异常。

(三)第三种方式

css 复制代码
var fm3: String? = format(str)
println(fm3?.length)

在这里,我们将 format(str) 的返回值声明为可空类型 String?,表示该变量可能为 null。因此,在打印 fm3?.length 时,使用了安全调用运算符 ?. 来确保当 fm3 为 null 时不会出现空指针异常。如果 fm3 不为 null,则返回 fm3 的长度;如果 fm3 为 null,则直接返回 null。

三、Kotlin 没有静态变量与静态方法

在Kotlin中没有传统的静态变量和静态方法,但是有一些替代方案可以实现类似的功能。

对于静态变量,Kotlin建议使用"伴随对象"(companion object)来模拟静态成员变量。伴随对象可以访问它所属的类的私有成员,而且只有在第一次访问时才会创建。可以使用companion object关键字来声明一个伴随对象。

对于静态方法,可以在伴随对象内部定义一个函数,该函数可以作为伴随对象的方法调用。可以使用@JvmStatic注解来强制将该函数暴露给Java代码。

例如:

kotlin 复制代码
class MyClass {
    companion object {
        const val MY_CONSTANT = "Hello, World!"
        
        fun myFunction() {
            // function implementation
        }
        
        @JvmStatic
        fun myStaticFunction() {
            // function implementation
        }
    }
}

在 Kotlin 中可以使用以下方式访问伴随对象的成员:

vbnet 复制代码
MyClass.MY_CONSTANT
MyClass.myFunction()
MyClass.myStaticFunction() 

在 Java 中可以使用以下方式访问伴随对象的成员:

ini 复制代码
System.out.println(MyClass.MY_CONSTANT);
MyClass.Companion.myFunction();
MyClass.myStaticFunction();

注意,伴随对象是一个单例,这意味着它的成员在整个应用程序中只有一个实例,并且可以通过对象名称或类名称直接访问。

相关推荐
长亭外的少年12 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
JIAY_WX12 小时前
kotlin
开发语言·kotlin
麦田里的守望者江20 小时前
KMP 中的 expect 和 actual 声明
android·ios·kotlin
菠菠萝宝1 天前
【YOLOv8】安卓端部署-1-项目介绍
android·java·c++·yolo·目标检测·目标跟踪·kotlin
恋猫de小郭1 天前
Kotlin Multiplatform 未来将采用基于 JetBrains Fleet 定制的独立 IDE
开发语言·ide·kotlin
枫__________2 天前
kotlin 协程 job的cancel与cancelAndJoin区别
android·开发语言·kotlin
鸠摩智首席音效师2 天前
如何在 Ubuntu 上配置 Kotlin 应用环境 ?
linux·ubuntu·kotlin
jikuaidi6yuan4 天前
Java与Kotlin在鸿蒙中的地位
java·kotlin·harmonyos
liulanba4 天前
Kotlin的data class
前端·微信·kotlin
小白学大数据4 天前
使用OkHttp进行HTTPS请求的Kotlin实现
爬虫·python·okhttp·https·kotlin