最近,一股「请愿风」席卷了 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))) // 30Java 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") // ¥47500Java 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