从 Java 到 Kotlin 的入门学习

前言

在 Android 开发领域,Kotlin 已经从一个备选语言成长为事实上的标准。根据 Google 的官方数据,超过 95% 的顶级 Android 应用都在使用 Kotlin。那么,是什么让 Kotlin 在短短几年内获得如此广泛的采纳?本文将从技术角度深入分析 Kotlin 的核心特性,帮助 Java 开发者快速入门这门现代语言。

Kotlin 在当今开发中的定位

多平台能力

Kotlin 已从单纯的 JVM 语言发展为真正的多平台解决方案:

  • Android 开发:官方首选语言
  • 后端开发:Spring Framework 5+ 原生支持
  • 跨平台移动开发:Kotlin Multiplatform Mobile (KMM)
  • 前端开发:可编译为 JavaScript
  • 原生开发:通过 Kotlin/Native 编译为机器码

与 Java 的完美互操作

Kotlin 最大的优势之一是与 Java 的 100% 互操作性。这意味着:

  • 可以直接在 Kotlin 中调用 Java 代码
  • 现有 Java 库和框架无需修改即可使用
  • 团队可以渐进式地从 Java 迁移到 Kotlin

Kotlin 核心特性

不可变性优先:val vs var

在 Kotlin 中,变量声明不仅仅是语法差异,更是编程理念的体现。

kotlin 复制代码
// val - 只读变量(推荐优先使用)
val name = "Kotlin"        // 类型推断为 String
val age: Int = 25          // 显式声明类型
name = "Java"              // 编译错误!不能重新赋值

// var - 可变变量
var count = 0				
count = 1                  // 可以重新赋值
count = "hello"            // 编译错误!类型不能改变

等价的 Java 代码

java 复制代码
// Java
final String name = "Kotlin";     // 对应 val
int count = 0;                    // 对应 var

最佳实践建议

  • 默认使用 val,只有在确实需要改变值时使用 var
  • 不可变变量使代码更易于推理和线程安全
  • 约 70-80% 的变量应该声明为 val

空安全:告别 NullPointerException

Kotlin 的类型系统在编译时区分可空和非空类型,从根本上解决了 NPE(空指针异常)问题。

可空类型声明

kotlin 复制代码
var name: String = "Kotlin"
name = null  // 编译错误!String 类型不能为 null

var nullableName: String? = "Kotlin"  // String? 表示可空类型
nullableName = null  // 正确

安全调用操作符 ?.

只有在对象非 null 时才调用方法或访问属性。

kotlin 复制代码
val str: String? = null
val length = str?.length    // 如果 str 为 null,返回 null;否则返回 str.length
println(length)             // 输出:null

val str2: String? = "Hello"
val length2 = str2?.length  // 返回 5

等价的 Java 代码

java 复制代码
String str = null;
Integer length = str != null ? str.length() : null;

链式调用更能体现优势:

kotlin 复制代码
// Kotlin - 简洁明了
val city = user?.address?.city?.name

// Java - 冗长繁琐
String city = null;
if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        City cityObj = address.getCity();
        if (cityObj != null) {
            city = cityObj.getName();
        }
    }
}

Elvis 操作符 ?:

提供默认值,当左侧为 null 时使用右侧的值。

kotlin 复制代码
val name: String? = null
val displayName = name ?: "Unknown"  // 如果 name 为 null,使用 "Unknown"
println(displayName)  // 输出:Unknown

// 常用于提前返回
fun processUser(user: User?) {
    val validUser = user ?: return  // 如果 user 为 null,直接返回
    // 继续处理 validUser...
}

等价的 Java 代码

java 复制代码
String name = null;
String displayName = name != null ? name : "Unknown";

// 或使用 Java 8+
String displayName = Optional.ofNullable(name).orElse("Unknown");

非空断言(谨慎使用)

强制将可空类型转换为非空类型,如果实际为 null 则抛出 NPE。

kotlin 复制代码
val name: String? = getName()
val length = name!!.length  // 如果 name 为 null,抛出 KotlinNullPointerException

警告:!! 是在向编译器说"我比你更清楚",应该尽量避免使用。如果发现代码中有很多 !!,说明设计可能有问题。

智能类型转换:让代码更流畅

类型检查操作符 is

kotlin 复制代码
fun describe(obj: Any): String {
    if (obj is String) {
        // 在这个代码块中,obj 自动转换为 String 类型
        return "String of length ${obj.length}"
    }
    if (obj is Int) {
        // obj 自动转换为 Int 类型
        return "Int with value $obj"
    }
    return "Unknown type"
}

等价的 Java 代码

java 复制代码
public String describe(Object obj) {
    if (obj instanceof String) {
        String str = (String) obj;  // 需要显式转换
        return "String of length " + str.length();
    }
    if (obj instanceof Integer) {
        Integer num = (Integer) obj;  // 需要显式转换
        return "Int with value " + num;
    }
    return "Unknown type";
}

when 表达式(增强版 switch)

kotlin 复制代码
fun getDescription(obj: Any) = when (obj) {
    is String -> "String of length ${obj.length}"
    is Int -> "Int with value $obj"
    is List<*> -> "List of size ${obj.size}"
    in 1..10 -> "Number in range 1-10"
    else -> "Unknown type"
}

函数式编程:Lambda 表达式

Kotlin 对函数式编程提供了一流支持,Lambda 表达式语法更加简洁。

基本语法

kotlin 复制代码
// Lambda 表达式的完整形式
val sum: (Int, Int) -> Int = { a, b -> a + b }

// 调用
println(sum(3, 5))  // 输出:8

// 如果只有一个参数,可以使用隐式的 it
val double: (Int) -> Int = { it * 2 }
println(double(5))  // 输出:10

集合操作

这是 Kotlin 最常用的场景之一:、

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)

// 过滤
val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers)  // [2, 4]

// 映射
val doubled = numbers.map { it * 2 }
println(doubled)  // [2, 4, 6, 8, 10]

// 链式调用
val result = numbers
    .filter { it % 2 == 0 }	// [2, 4]
    .map { it * it }	// [4, 16]
    .sum()
println(result)  // 20

等价的 Java 代码(Java 8+)

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 过滤
List<Integer> evenNumbers = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());

// 链式调用
int result = numbers.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * n)
    .mapToInt(Integer::intValue)
    .sum();

高阶函数

函数可以作为参数传递,也可以作为返回值:

kotlin 复制代码
// 接受函数作为参数
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

// 使用
val sum = calculate(5, 3) { x, y -> x + y }      // 8
val product = calculate(5, 3) { x, y -> x * y }  // 15

// 返回函数
fun makeMultiplier(factor: Int): (Int) -> Int {
    return { number -> number * factor }
}

val triple = makeMultiplier(3)
println(triple(5))  // 15

扩展函数:为既有类添加功能

扩展函数允许你在不修改原类的情况下为其添加新方法。

kotlin 复制代码
// 为 String 类添加扩展函数
fun String.removeSpaces(): String {
    return this.replace(" ", "")
}

// 使用
val text = "Hello World"
println(text.removeSpaces())  // "HelloWorld"

// 为泛型类型添加扩展
fun <T> List<T>.secondOrNull(): T? {
    return if (this.size >= 2) this[1] else null
}

val numbers = listOf(1, 2, 3)
println(numbers.secondOrNull())  // 2

这种机制使得 Kotlin 标准库能够为 Java 类添加许多便利方法,而无需修改原始代码。

属性访问:告别冗余的 getter/setter

kotlin 复制代码
// Kotlin
class Person {
    var name: String = ""
    val age: Int = 0  // 只读属性(只有 getter)
}

val person = Person()
person.name = "Alice"  // 直接赋值
println(person.name)   // 直接访问

等价的 Java 代码

java 复制代码
public class Person {
    private String name = "";
    private final int age = 0;
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
}

Person person = new Person();
person.setName("Alice");
System.out.println(person.getName());

数据类:一行代码搞定 POJO

kotlin 复制代码
// Kotlin - 一行代码
data class User(val name: String, val age: Int)

// 自动生成:
// - equals() / hashCode()
// - toString()
// - copy()
// - componentN() for destructuring

等价的 Java 代码需要 50+ 行(或使用 Lombok)

java 复制代码
public class User {
    private final String name;
    private final int age;
    
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // getter, equals, hashCode, toString...
    // 省略约 40 行代码
}

区间和 in 操作符

kotlin 复制代码
// 区间表达式
val range = 1..10          // 1 到 10(包含)
val range2 = 1 until 10    // 1 到 9(不包含 10)

// in 操作符
if (5 in range) {
    println("5 is in range")
}

// 遍历
for (i in 1..5) {
    println(i)
}

// 检查是否在集合中
val numbers = listOf(1, 2, 3, 4, 5)
if (3 in numbers) {
    println("3 is in the list")
}

实战案例:对比 Java 和 Kotlin

让我们通过一个真实场景来对比两种语言的代码风格。

场景:处理用户列表,过滤成年人并返回姓名

Java 版本

java 复制代码
public class UserProcessor {
    public List<String> getAdultNames(List<User> users) {
        if (users == null) {
            return Collections.emptyList();
        }
        
        List<String> result = new ArrayList<>();
        for (User user : users) {
            if (user != null && user.getAge() >= 18) {
                String name = user.getName();
                if (name != null) {
                    result.add(name);
                }
            }
        }
        return result;
    }
}

Kotlin 版本

kotlin 复制代码
class UserProcessor {
    fun getAdultNames(users: List<User>?): List<String> {
        return users
            ?.filter { it.age >= 18 }
            ?.map { it.name }
            ?: emptyList()
    }
}

// 或者更简洁的单表达式函数
fun getAdultNames(users: List<User>?) = 
    users?.filter { it.age >= 18 }?.map { it.name } ?: emptyList()
相关推荐
alexhilton17 小时前
Compose中的ContentScale:终极可视化指南
android·kotlin·android jetpack
jzlhll12318 小时前
kotlin Flow first() last()总结
开发语言·前端·kotlin
符哥20081 天前
充电桩 WiFi 局域网配网(Android/Kotlin)流程、指令及实例说明文档
android·开发语言·kotlin
大傻^1 天前
SpringAI2.0 Null Safety 实战:JSpecify 注解体系与 Kotlin 互操作
android·开发语言·人工智能·kotlin·springai
jzlhll1231 天前
Kotlin Mutex vs Java ReentrantLock vs synchronized
java·开发语言·kotlin
Kapaseker1 天前
一杯 Kotlin 美式品味 object 声明
android·kotlin
俩个逗号。。1 天前
Kotlin 扩展函数详解
开发语言·kotlin
su1ka1112 天前
Kotlin(3)基本语法
kotlin
su1ka1112 天前
Kotlin(4)面向对象
kotlin
鹧鸪晏2 天前
搞懂 kotlin 泛型 out 和 in 关键字
android·kotlin