此页面通过共享每种语言的并排示例,对 Java 和 Scala 编程语言进行了比较。 它适用于了解 Java 并希望了解 Scala 的程序员,特别是通过 Scala 特性与 Java 特性的对比来了解。
概述
在进入示例之前,第一部分提供了以下部分的相对简短的介绍和总结。 它从高层次上介绍了 Java 和 Scala 之间的异同,然后介绍了您在每天编写代码时会遇到的差异。
高层次的相似性
在高层次上,Scala 与 Java 有以下相似之处:
- Scala代码编译成_.class_文件,打包成JAR文件,运行在JVM上
- 这是一种面向对象编程 (OOP) 语言
- 它是静态类型的
- 两种语言都支持 lambdas 和 高阶函数
- 它们都可以与 IntelliJ IDEA 和 Microsoft VS Code 等 IDE 一起使用
- 可以使用 Gradle、Ant 和 Maven 等构建工具构建项目
- 它具有用于构建服务器端、网络密集型应用程序的出色库和框架,包括 Web 服务器应用程序、微服务、机器学习等(参见 “Awesome Scala” 列表)
- Java 和 Scala 都可以使用 Scala 库:
- 他们可以使用 Akka actor 库 来构建基于 actor 的并发系统,并使用 Apache Spark 来构建数据密集型应用程序
- 他们可以使用 Play Framework 开发服务器端应用程序
- 您可以使用 GraalVM 将您的项目编译为本机可执行文件
- Scala 可以无缝使用为 Java 开发的大量库
高层次的差异
同样在高层次上,Java 和 Scala 之间的区别是:
- Scala 语法简洁易读;我们称之为_表现力_
- 虽然它是静态类型的,但 Scala 经常感觉像是一门动态语言
- Scala 是一种纯 OOP 语言,因此每个对象都是类的一个实例,而像运算符一样的符号
+
和+=
是真正的方法;这意味着您可以创建自己的运算符 - 除了是纯OOP语言,Scala还是纯FP语言;实际上,它鼓励 OOP 和 FP 的融合,具有用于逻辑的函数和用于模块化的对象
- Scala 拥有一整套不可变集合,包括
List
、Vector
和不可变的Map
和Set
实现 - Scala 中的一切都是一个_表达式_:像
if
语句、for
循环、match
表达式,甚至try
/catch
表达式都有返回值 - Scala 习惯上倾向缺省使用不可变性:鼓励您使用不可变(
final
)变量和不可变集合 - 惯用的 Scala 代码不使用
null
,因此不会遭受NullPointerException
- Scala 生态系统在 sbt、Mill 等中还有其他 构建工具
- 除了在 JVM 上运行之外,Scala.js 项目允许您使用 Scala 作为 JavaScript 替代品
- Scala Native 项目添加了低级结构,让您可以编写“系统”级代码,也可以编译为本机可执行文件
编程层次差异
最后,这些是您在编写代码时每天都会看到的一些差异:
- Scala 的语法极其一致
- 变量和参数被定义为
val
(不可变,如Java中的final
)或var
(可变) - 类型推导 让您的代码感觉是动态类型的,并有助于保持您的代码简洁
- 除了简单的
for
循环之外,Scala 还具有强大的for
comprehensions,可以根据您的算法产生结果 - 模式匹配和
match
表达式将改变你编写代码的方式 - 默认情况下编写不可变代码会导致编写_表达式_而不是_语句_;随着时间的推移,您会发现编写表达式可以简化您的代码(和您的测试)
- 顶层定义 让您可以将方法、字段和其他定义放在任何地方,同时也带来简洁、富有表现力的代码
- 您可以通过将多个 traits “混合”到类和对象中来创建_混搭_(特征类似于 Java 8 和更新版本中的接口)
- 默认情况下类是封闭的,支持 Joshua Bloch 在 Effective Java 的习惯用法,“Design and document for inheritance or else forbid it”
- Scala 的 上下文抽象 和 术语推导 提供了一系列特性:
- Scala 拥有最先进的第三方开源函数式编程库
- Scala 样例类就像 Java 14 中的记录;它们可以帮助您在编写 FP 代码时对数据进行建模,并内置对模式匹配和克隆等概念的支持
- 由于名称参数、中缀符号、可选括号、扩展方法和 高阶函数 等功能,您可以创建自己的“控制结构”和 DSL
- Scala 文件不必根据它们包含的类或 trait 来命名
- 许多其他好东西:伴生类和对象、宏、联合 和 交集、数字字面量、多参数列表、参数的默认值、命名参数等
用例子来进行特性对比
鉴于该介绍,以下部分提供了 Java 和 Scala 编程语言功能的并排比较。
OOP 风格的类和方法
本节提供了与 OOP 风格的类和方法相关的特性的比较。
注释:
//
|
//
|
OOP 风格类,主构造函数:
Scala不遵循JavaBeans标准,因此我们在这里展示的Java代码 与它后面的Scala代码等效,而不是显示以JavaBeans风格编写 的Java代码。
class Person {
|
class Person (
|
辅助构造函数:
public class Person {
|
class Person (
|
类默认是封闭的:
“Plan for inheritance or else forbid it.”
final class Person
|
class Person
|
为扩展开放的类:
class Person
|
open class Person
|
单行方法:
public int add(int a, int b) {
|
def add(a: Int, b: Int): Int = a + b
|
多行方法:
public void walkThenRun() {
|
def walkThenRun() =
|
不可变字段:
final int i = 1;
|
val i = 1
|
可变字段:
int i = 1;
|
var i = 1
|
接口、trait 和继承
本节将Java接口与Scala trait 进行比较,包括类如何扩展接口和 trait。
接口/trait:
public interface Marker;
|
trait Marker
|
简单接口:
public interface Adder {
|
trait Adder:
|
有实体方法的接口:
public interface Adder {
|
trait Adder:
|
继承:
class Dog extends Animal implements HasLegs, HasTail
|
class Dog extends Animal, HasLegs, HasTail
|
扩展多个接口
这些接口和特征具有具体的、已实现的方法(默认方法):
interface Adder {
|
trait Adder:
|
混搭:
N/A |
class DavidBanner
|
控制结构
本节比较在 Java 和 Scala 中的控制结构。
if
语句,单行:
if (x == 1) { System.out.println(1); }
|
if x == 1 then println(x)
|
if
语句,多行:
if (x == 1) {
|
if x == 1 then
|
if, else if, else:
if (x < 0) {
|
if x < 0 then
|
if
作为方法体:
public int min(int a, int b) {
|
def min(a: Int, b: Int): Int =
|
从 if
返回值:
在 Java 中调用_三元运算符_:
int minVal = (a < b) ? a : b;
|
val minValue = if a < b then a else b
|
while
循环:
while (i < 3) {
|
while i < 3 do
|
for
循环,单行:
for (int i: ints) {
|
//preferred
|
for
循环,多行:
for (int i: ints) {
|
for
|
for
循环,多生成器:
for (int i: ints1) {
|
for
|
带守卫(if
)表达式的生成器:
List ints =
|
for
|
for
comprehension:
N/A |
val list =
|
switch/match:
String monthAsString = "";
|
val monthAsString = day match
|
switch/match, 每个情况下多个条件:
String numAsString = "";
|
val numAsString = i match
|
try/catch/finally:
try {
|
try
|
集合类
本节比较 Java 和 Scala 里的集合类。
不可变集合类
如何创建不可变集合实例的例子。
Sequences:
List strings = List.of("a", "b", "c");
|
val strings = List("a", "b", "c")
|
Sets:
Set set = Set.of("a", "b", "c");
|
val set = Set("a", "b", "c")
|
Maps:
Map map = Map.of(
|
val map = Map(
|
可变集合类
Scala 在其 scala.collection.mutable 包中有可变集合类,例如 ArrayBuffer
、Map
和 Set
。
在 导入它们 到当前作用域之后,创建它们就像刚刚显示的不可变 List
、Vector
、Map
和 Set
示例一样。
Scala 还有一个 Array
类,您可以将其视为 Java array
原始类型的包装器。
一个 Scala Array[A]
映射到一个 Java A[]
,所以你可以认为这个是 Scala Array[String]
:
val a = Array("a", "b")
这个追溯到 Java 的 String[]
:
String[] a = {"a", "b"};
但是,Scala Array
还具有您期望在 Scala 集合中使用的所有函数方法,包括 map
和 filter
:
val nums = Array(1, 2, 3, 4, 5)
val doubledNums = nums.map(_ * 2)
val 过滤Nums = nums.filter(_ > 2)
因为 Scala Array
的表示方式与 Java array
相同,所以您可以轻松地在 Scala 代码中使用返回数组的 Java 方法。
尽管讨论了
Array
,但请记住,在 Scala 中通常有可能更适合的Array
替代品。 数组对于与其他语言(Java、JavaScript)的互操作很有用,并且在编写需要从底层平台获得最大性能的低级代码时也很有用。但总的来说,当你需要使用序列时,Scala 的习惯用法是更喜欢像Vector
和List
这样的不可变序列,然后在你真的需要可变序列时使用ArrayBuffer
。
您还可以使用 Scala CollectionConverters
对象在 Java 和 Scala 集合类之间进行转换。
在不同的包中有两个对象,一个用于从 Java 转换为 Scala,另一个用于从 Scala 转换为 Java。
下表显示了可能的转换:
Java | Scala |
---|---|
java.util.Collection | scala.collection.Iterable |
java.util.List | scala.collection.mutable.Buffer |
java.util.Set | scala.collection.mutable.Set |
java.util.Map | scala.collection.mutable.Map |
java.util.concurrent.ConcurrentMap | scala.collection.mutable.ConcurrentMap |
java.util.Dictionary | scala.collection.mutable.Map |
集合类的方法
由于能够将 Java 集合视为流,Java 和 Scala 现在可以使用许多相同的通用函数方法:
map
filter
forEach
/foreach
findFirst
/find
reduce
如果您习惯在 Java 中将这些方法与 lambda 表达式一起使用,您会发现在 Scala 的 集合类 上使用相同的方法很容易。
Scala 也有_数十个_其他 集合方法,包括 head
、tail
、drop
、take
、distinct
、flatten
等等。
起初你可能想知道为什么会有这么多方法,但是在使用 Scala 之后你会意识到_因为_有这些方法,你很少需要再编写自定义的 for
循环了。
(这也意味着你也很少需要_读_自定义的 for
循环。
因为开发人员倾向于在_读_代码上花费的时间是_编写_代码的十倍,这很重要。)
元组
Java 元组是这样创建的:
Pair<String, Integer> pair =
new Pair<String, Integer>("Eleven", 11);
Triplet<String, Integer, Double> triplet =
Triplet.with("Eleven", 11, 11.0);
Quartet<String, Integer, Double,Person> triplet =
Quartet.with("Eleven", 11, 11.0, new Person("Eleven"));
其他 Java 元组名称是 Quintet、Sextet、Septet、Octet、Ennead、Decade。
Scala 中任何大小的元组都是通过将值放在括号内来创建的,如下所示:
val a = ("eleven")
val b = ("eleven", 11)
val c = ("eleven", 11, 11.0)
val d = ("eleven", 11, 11.0, Person("Eleven"))
枚举
本节比较 Java 和 Scala 中的枚举。
基本枚举:
enum Color {
|
enum Color:
|
参数化的枚举:
enum Color {
|
enum Color(val rgb: Int):
|
用户定义的枚举成员:
enum Planet {
|
enum Planet(
|
异常和错误处理
本节介绍 Java 和 Scala 中的异常处理之间的差异。
Java 使用检查异常
Java 使用检查的异常,因此在 Java 代码中,您历来编写过 try
/catch
/finally
块,以及方法上的 throws
子句:
public int makeInt(String s)
throws NumberFormatException {
// code here to convert a String to an int
}
Scala 不使用检查异常
Scala 的习惯用法是_不_使用这样的检查异常。
在处理可能抛出异常的代码时,您可以使用 try
/catch
/finally
块从抛出异常的代码中捕获异常,但是如何从那里开始的方式是不同的。
解释这一点的最好方法是说 Scala 代码是由具有返回值的_表达式_组成的。 其结果是,最终你写代就像写一系列代数表达式:
val a = f(x)
val b = g(a,z)
val c = h(b,y)
这很好,它只是代数。 您创建方程来解决小问题,然后组合方程来解决更大的问题。
非常重要的是——正如你在代数课程中所记得的那样——代数表达式不会短路——它们不会抛出会破坏一系列方程的异常。
因此,在 Scala 中,我们的方法不会抛出异常。
相反,它们返回像 Option
这样的类型。
例如,这个 makeInt
方法捕获一个可能的异常并返回一个 Option
值:
def makeInt(s: String): Option[Int] =
try
Some(s.toInt)
catch
case e: NumberFormatException => None
Scala Option
类似于 Java Optional
类。
如图所示,如果 string 到 int 的转换成功,则在 Some
值中返回 Int
,如果失败,则返回 None
值。
Some
和 None
是 Option
的子类型,因此该方法被声明为返回 Option[Int]
类型。
当您有一个 Option
值时,例如 makeInt
返回的值,有很多方法可以使用它,具体取决于您的需要。
此代码显示了一种可能的方法:
makeInt(aString) match
case Some(i) => println(s"Int i = $i")
case None => println(s"Could not convert $aString to an Int.")
Option
在 Scala 中很常用,它内置在标准库的许多类中。
其他类似的类的集合,例如 Try/Success/Failure 和 Either/Left/Right,提供了更大的灵活性。
有关在 Scala 中处理错误和异常的更多信息,请参阅 函数式错误处理 部分。
Scala 独有的概念
以上就是 Java 和 Scala 语言的比较。
Scala 中还有其他一些概念目前在 Java 11 中是没有的。 这包括:
Contributors to this page:
Contents
- 导言
- Scala 3 特性
- 为什么是 Scala 3 ?
- Scala 的味道
- Hello, World!
- The REPL
- 变量和数据类型
- 控制结构
- 领域建模
- 方法
- 头等函数
- 单例对象
- 集合
- 上下文抽象
- 顶层定义
- 总结
- 类型初探
- 字符串插值
- 控制结构
- 领域建模
- 工具
- OOP 领域建模
- 函数式领域建模
- 方法
- 方法特性
- main 方法
- 总结
- 函数
- 匿名函数
- 函数变量
- Eta 扩展
- 高阶函数
- 自定义 map 函数
- 创建可以返回函数的方法
- 总结
- 打包和导入
- Scala 集合
- 集合类型
- 集合方法
- 总结
- 函数式编程
- 什么是函数式编程?
- 不可变值
- 纯函数
- 函数是值
- 函数式错误处理
- 总结
- 类型和类型系统
- 类型推断
- 泛型
- 相交类型
- 联合类型
- 代数数据类型
- 型变
- 不透明类型
- 结构化类型
- 依赖函数类型
- 其他类型
- 上下文抽象
- 扩展方法
- Given 实例和 Using 语句
- 上下文绑定
- Given 导入
- 实现类型类
- 多元相等性
- 隐式转换
- 总结
- 并发
- Scala 工具
- 使用 sbt 构建和测试 Scala 项目
- worksheet
- 与 Java 交互
- 向 Java 开发者介绍Scala
- Scala for JavaScript Developers
- Scala for Python Developers
- 下一步去哪