从 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()
相关推荐
阿健君10 小时前
Java/Kotlin 泛型
kotlin
来来走走12 小时前
kotlin学习 基础知识一览
android·开发语言·kotlin
雨白18 小时前
StateFlow 与 SharedFlow:在协程中管理状态与事件
android·kotlin
tangweiguo0305198721 小时前
ProcessLifecycleOwner 完全指南:优雅监听应用前后台状态
android·kotlin
消失的旧时光-19431 天前
Kotlin reified泛型 和 Java 泛型 区别
java·kotlin·数据
雨白2 天前
Flow 的异常处理与执行控制
android·kotlin
ClassOps2 天前
Gradle Groovy 和 Kotlin kts 语法对比
android·kotlin·gradle·groovy
用户69371750013842 天前
⚡Kotlin 五大神器完全解析:let、with、run、apply、also 一次搞懂,面试官都笑了!
android·kotlin
木易 士心2 天前
Spring Boot + Kotlin + Gradle 构建现代化后端应用
spring·kotlin