TempGo
发布于 2025-11-10 / 58 阅读
0
0

Java 25 vs Scala: 为什么 Scala 领先了 20 年

最近,一股「请愿风」席卷了 Reddit 和 X 的技术圈。Spring 框架的布道师 Josh Long、Kotlin 核心开发者等业界大佬纷纷发声,公开呼吁 Java 加速引入各种「现代化」特性。

有意思的是,这些被热切期待的「未来特性」,在 Scala 中早已是十几年前就实现的标准功能。

笔者上一次写 Scala 还是上次,只能「浅显」的从实际代码出发,对比分析 Scala 与 Java 在一些语法糖上的差异,本文纯属一乐。

Java 8 (2014): 函数式编程的范式转折

Java 8 在 Java 语言发展史上具有里程碑意义,其核心变革在于引入了 Lambda 表达式和 Stream API,标志着 Java 正式开启向函数式编程范式的转型。

Scala (2004) 的原生函数式设计:

// Scala 集合操作是可重复使用的
val animals = List("pig", "cat", "dog")

// 可以无限次对同一集合进行操作
val upper = animals.map(_.toUpperCase)  // List("PIG", "CAT", "DOG")
val long = animals.filter(_.length > 3)  // List("dog")

// 链式操作自动进行融合优化
val result = animals.filter(_.length > 2).map(_.toUpperCase)
// List("PIG", "CAT", "DOG")

Java Stream 的使用约束:

// Java Stream 只能消费一次
List<String> animals = List.of("pig", "cat", "dog");
Stream<String> stream = animals.stream();

List<String> upper = stream
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// 尝试重用会抛出 IllegalStateException
// List<String> long = stream.filter(s -> s.length() > 3).toList();  // 报错!

模式匹配:从运行时检查到编译期

Scala 检查机制

Scala 的模式匹配不仅仅是语法糖,更是编译器级别的类型安全保障。通过 sealed trait 和 case class 的组合,Scala 编译器能够在编译期进行穷尽性检查。

Scala 示例 (自 2004 年起):

// 1. 基础模式匹配
def analyze(data: Any): String = data match {
  case List("pig", "cat", "dog") => "三只动物"
  case (x: Int, y: Int) => s"坐标: ($x, $y)"
  case Some("lengleng") => "用户 lengleng"
  case s: String if s.length > 5 => "长字符串"
  case _ => "其他"
}

// 2. 支付方式 (编译器强制穷尽性检查)
sealedtrait PaymentMethod
caseclass CreditCard(holder: String) extends PaymentMethod
caseclass Alipay(account: String) extends PaymentMethod
caseobject Cash extends PaymentMethod

def pay(method: PaymentMethod, amount: Int): String = method match {
  case CreditCard(name) => s"$name 的信用卡支付 ¥$amount"
  case Alipay(acc) => s"支付宝 $acc 支付 ¥$amount"
  case Cash => s"现金支付 ¥$amount"
  // 缺少 case 时编译器会警告
}

// 示例
pay(CreditCard("lengleng"), 100)  // "lengleng 的信用卡支付 ¥100"
pay(Alipay("pig@pigx.vip"), 200)  // "支付宝 pig@pigx.vip 支付 ¥200"

// 3. 表达式求值器
sealedtrait Expr
caseclass Num(value: Int) extends Expr
caseclass Add(left: Expr, right: Expr) extends Expr

def eval(expr: Expr): Int = expr match {
  case Num(n) => n
  case Add(l, r) => eval(l) + eval(r)
}

eval(Add(Num(10), Num(20)))  // 30

Java 21 的模式匹配实现:

// 1. instanceof 模式匹配
public String analyze(Object obj) {
    if (obj instanceof String s && s.length() > 5) {
        return "长字符串:" + s;
    } else if (obj instanceof Integer i) {
        return "数字:" + i;
    }
    return "其他";
}

// 2. switch 表达式 + 模式匹配
public String pay(Object payment, int amount) {
    return switch (payment) {
        case CreditCard(String holder) ->
            holder + " 的信用卡支付 ¥" + amount;
        case Alipay(String account) ->
            "支付宝 " + account + " 支付 ¥" + amount;
        case null -> "无效支付方式";
        default -> "不支持的支付方式";
    };
}

// 3. Record 模式解构
record Point(int x, int y) {}

public String analyzePoint(Object obj) {
    if (obj instanceof Point(int x, int y)) {
        return "坐标:(" + x + ", " + y + ")";
    }
    return "非坐标";
}

2. 函数与 Lambda 表达式

Scala 示例 (自 2004 年起):

// 1. 函数字面量
val greet = (name: String) => s"Hello, $name"
val add: (Int, Int) => Int = _ + _

greet("lengleng")  // "Hello, lengleng"
add(10, 20)  // 30

// 2. 柯里化
def greetWith(greeting: String)(name: String): String = s"$greeting, $name"
val sayHi = greetWith("Hi") _
sayHi("pig")  // "Hi, pig"

// 3. 函数组合
val addOne: Int => Int = _ + 1
val double: Int => Int = _ * 2

val addThenDouble = double compose addOne
addThenDouble(5)  // (5 + 1) * 2 = 12

// 4. 集合操作
val users = List("lengleng", "pig", "cat")
users.filter(_.length > 3).map(_.toUpperCase)
// List("LENGLENG")

// 5. 实战:计算工资
case class Employee(name: String, dept: String, salary: Int)

val employees = List(
  Employee("lengleng", "Tech", 50000),
  Employee("pig", "Tech", 45000),
  Employee("cat", "Sales", 30000)
)

val techAvg = employees
  .filter(_.dept == "Tech")
  .map(_.salary)
  .sum / employees.count(_.dept == "Tech")

println(s"Tech 部门平均工资: ¥$techAvg")  // ¥47500

Java 21 示例:

// Lambda 表达式
Function<String, String> greet = name -> "Hello, " + name;
BinaryOperator<Integer> add = (x, y) -> x + y;

greet.apply("lengleng");  // "Hello, lengleng"
add.apply(10, 20);  // 30

// 函数组合
Function<Integer, Integer> addOne = x -> x + 1;
Function<Integer, Integer> doubleValue = x -> x * 2;
Function<Integer, Integer> addThenDouble = doubleValue.compose(addOne);

addThenDouble.apply(5);  // 12

// 集合操作
List<String> users = List.of("lengleng", "pig", "cat");
users.stream()
    .filter(s -> s.length() > 3)
    .map(String::toUpperCase)
    .toList();  // ["LENGLENG"]

// 员工数据处理
record Employee(String name, String dept, int salary) {}

double techAvg = employees.stream()
    .filter(e -> e.dept().equals("Tech"))
    .mapToInt(Employee::salary)
    .average()
    .orElse(0.0);

3. 不可变集合

为什么默认不可变

在多线程环境中,不可变集合从根本上避免了竞态条件。

Scala 示例 (自 2004 年起):

// 不可变集合是默认选择
val animals = List("pig", "cat", "dog")

// 所有修改操作返回新集合
val newList = animals :+ "bird"     // List("pig", "cat", "dog", "bird")
val prepended = "fish" :: animals   // List("fish", "pig", "cat", "dog")
println(animals)                    // List("pig", "cat", "dog") - 原集合不变

// Map 操作
val users = Map("lengleng" -> 100, "pig" -> 200)
val updated = users + ("cat" -> 150)

// 结构共享优化:只创建变更节点,其余共享
val bigList = (1 to 1000000).toList
val bigListPlus = bigList :+ 1000001  // O(1) 时间复杂度

// Vector: O(log N) 随机访问
val vec = Vector("pig", "cat", "dog")
val vec2 = vec.updated(1, "bird")  // Vector("pig", "bird", "dog")

Java 21 示例:

// 不可变列表 (Java 9+)
List<String> animals = List.of("pig", "cat", "dog");

// 尝试修改会抛出异常
try {
    animals.add("bird");
} catch (UnsupportedOperationException e) {
    System.out.println("不可变列表无法修改");
}

// 必须创建可变副本
List<String> mutable = new ArrayList<>(animals);
mutable.add("bird");

// Java 的不可变集合没有结构共享
// 每次「修改」都需要 O(n) 完全拷贝

4. 类型推断

Scala 的强大类型推断

Scala 示例 (自 2004 年起):

// 局部变量类型推断
val name = "lengleng"           // String
val age = 18                    // Int
val users = List("pig", "cat")  // List[String]

// 泛型类型推断
val map = Map("pig" -> 100, "cat" -> 200)  // Map[String, Int]
val tuple = ("lengleng", 18, true)         // (String, Int, Boolean)

// 函数类型推断
val greet = (name: String) => s"Hi, $name"  // String => String

// 高阶函数类型推断
def twice[A](f: A => A, x: A): A = f(f(x))
val result = twice((x: Int) => x * 2, 5)  // 推断 A = Int, 结果 20

// 复杂嵌套泛型
val nested = List(List("pig", "cat"), List("dog"))  // List[List[String]]

Java 21 示例:

// 局部变量类型推断 (Java 10+)
var name = "lengleng";
var age = 18;

// var 的局限性
// - 不可用于方法参数
// - 不可用于方法返回类型
// - 不可用于字段

var users = List.of("pig", "cat");
var upper = users.stream()
    .map(String::toUpperCase)
    .toList();

5. 字符串插值与多行文本块

Scala 的灵活字符串处理

Scala 示例 :

// 多行文本 2004
val sql = """
  SELECT name, age
  FROM users
  WHERE dept = 'Tech'
  ORDER BY age DESC
"""

// 表达式插值
val price = 199.99
val quantity = 3
val total = s"Total: ${price * quantity}"
// "Total: 599.97"

Java 示例:

// 1. 文本块 (Java 17,)
String sql = """
    SELECT name, age
    FROM users
    WHERE dept = 'Tech'
    ORDER BY age DESC
    """;

// 2. 文本块 + 字符串拼接
String user = "lengleng";
String config = """
    server:
      host: localhost
      port: 8080
      admin: """ + user;

6. 密封类与代数数据类型 (ADT)

Scala 示例 (自 2004 年起):

// 代数数据类型:HTTP 响应
sealed trait HttpResponse
case class Ok(body: String) extends HttpResponse
case class NotFound(path: String) extends HttpResponse
case class Error(code: Int, msg: String) extends HttpResponse

def handle(resp: HttpResponse): String = resp match {
  case Ok(body) => s"成功: $body"
  case NotFound(path) => s"未找到: $path"
  case Error(code, msg) => s"错误 $code: $msg"
}

handle(Ok("Hello, lengleng"))           // "成功: Hello, lengleng"
handle(NotFound("/pig/profile"))        // "未找到: /pig/profile"
handle(Error(500, "Server Error"))      // "错误 500: Server Error"

Java 21 示例:

// Sealed 类 (Java 17+)
public sealed interface HttpResponse
    permits Ok, NotFound, Error {}

public record Ok(String body) implements HttpResponse {}
public record NotFound(String path) implements HttpResponse {}
public record Error(int code, String msg) implements HttpResponse {}

public static String handle(HttpResponse resp) {
    return switch (resp) {
        case Ok(String body) -> "成功:" + body;
        case NotFound(String path) -> "未找到:" + path;
        case Error(int code, String msg) -> "错误 " + code + ": " + msg;
    };
}

7. 并发模型

Scala 的多层次并发抽象

Scala 示例:

// 1. Future: 异步计算
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def fetchUser(name: String): Future[String] = Future {
  Thread.sleep(1000)
  s"User: $name"
}

// 组合多个 Future
val combined = for {
  u1 <- fetchUser("lengleng")
  u2 <- fetchUser("pig")
} yield (u1, u2)

combined.foreach(println)  // (User: lengleng, User: pig)

Java 21 示例 (虚拟线程):

// 虚拟线程执行器
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    var tasks = List.of("lengleng", "pig", "cat");

    var futures = tasks.stream()
        .map(name -> executor.submit(() -> {
            Thread.sleep(1000);
            return "User: " + name;
        }))
        .toList();

    for (var future : futures) {
        System.out.println(future.get());
    }
}

// 结构化并发 (Java 25)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    var user1 = scope.fork(() -> fetchUser("lengleng"));
    var user2 = scope.fork(() -> fetchUser("pig"));

    scope.join();
    System.out.println(user1.get());
    System.out.println(user2.get());
}

写在最后

从技术演进看,Scala 在许多核心语言特性上确实遥遥领先 Java。这种领先不仅体现在语法糖层面,更深入到类型系统设计、编译器优化、并发模型等底层架构。

然而,Java 的庞大生态、成熟工具链和企业级支持使其在实际工程中仍占主导地位。Java 21 的虚拟线程、模式匹配等特性,标志着 Java 正在积极拥抱现代编程范式。

来源:https://mp.weixin.qq.com/s/28sARkDtSJuXenWNYlbI4g


评论