有些功能已移除以簡化語言。這些變更大多數可以在 Scala 3 遷移編譯期間自動處理。
不相容性 | Scala 2.13 | Scala 3 遷移重寫 | Scalafix 規則 |
---|---|---|---|
符號文字 | 不建議使用 | ✅ | |
do -while 結構 |
✅ | ||
自動應用 | 不建議使用 | ✅ | ✅ |
值 eta 展開 | 不建議使用 | ✅ | ✅ |
any2stringadd 轉換 |
不建議使用 | ✅ | |
早期初始化器 | 不建議使用 | ||
存在類型 | 功能警告 | ||
@specialized | 不建議使用 |
符號文字
符號文字語法在 Scala 2.13 中不建議使用,並已在 Scala 3 中移除。但 scala.Symbol
類別仍然存在,因此每個字串文字都可以安全地替換為 Symbol
的應用。
這段程式碼無法使用 Scala 3 編譯
val values: Map[Symbol, Int] = Map('abc -> 1)
val abc = values('abc) // In Scala 3, Migration Warning: symbol literal 'abc is no longer supported
Scala 3 遷移編譯將程式碼重寫為
val values: Map[Symbol, Int] = Map(Symbol("abc") -> 1)
-val abc = values('abc)
+val abc = values(Symbol("abc"))
儘管 Symbol
類別在轉換期間很有用,但請注意它已過時,且將在未來的版本中從 scala-library
中移除。建議您第二步將每個 Symbol
用法替換為純文字字串 "abc"
或自訂專用類別。
do
-while
結構
在 新的控制語法 中,do
關鍵字已取得不同的意義。
為避免混淆,傳統的 do <body> while (<cond>)
結構已被捨棄。建議使用等效的 while ({ <body>; <cond> }) ()
,它可以跨編譯,或使用新的 Scala 3 語法 while { <body>; <cond> } do ()
。
以下程式碼片段無法使用 Scala 3 編譯。
do { // In Scala 3, Migration Warning: `do <body> while <cond>` is no longer supported
i += 1
} while (f(i) == 0)
Scala 3 遷移編譯會將其改寫為。
while ({ {
i += 1
} ; f(i) == 0}) ()
自動應用
自動應用是呼叫空括號方法的語法,例如 def toInt(): Int
,而不傳遞空引數清單。它在 Scala 2.13 中已過時,並在 Scala 3 中捨棄。
以下程式碼在 Scala 3 中無效
object Hello {
def message(): String = "Hello"
}
println(Hello.message) // In Scala 3, Migration Warning: method message must be called with () argument
Scala 3 遷移編譯會將其改寫為
object Hello {
def message(): String = "Hello"
}
-println(Hello.message)
+println(Hello.message())
自動應用在 Scala 3 參考文件 此頁面 中有詳細說明。
值 eta 展開
Scala 3 引入了 自動 Eta 展開,這將使方法到值語法 m _
過時。此外,Scala 3 不再允許將值 eta 展開為零元函式。
因此,以下程式碼片段在 Scala 3 中無效
val x = 1
val f: () => Int = x _ // In Scala 3, Migration Warning: The syntax `<function> _` is no longer supported;
Scala 3 遷移編譯會將其改寫為
val x = 1
-val f: () => Int = x _
+val f: () => Int = (() => x)
any2stringadd
轉換
隱含的 Predef.any2stringadd
轉換在 Scala 2.13 中已過時,並在 Scala 3 中捨棄。
以下程式碼片段在 Scala 3 中不再編譯。
val str = new AnyRef + "foo" // In Scala 3, Error: value + is not a member of Object
必須明確套用轉換為 String
,例如使用 String.valueOf
。
-val str = new AnyRef + "foo"
+val str = String.valueOf(new AnyRef) + "foo"
此重寫可透過 fix.scala213.Any2StringAdd
Scalafix 規則套用在 scala/scala-rewrites
中。
早期初始化器
早期初始化器在 Scala 2.13 中已棄用,並在 Scala 3 中移除。它們很少使用,而且大多用於彌補 特質參數 的不足,而特質參數現已在 Scala 3 中支援。
這就是為什麼以下程式碼在 Scala 3 中無法再編譯的原因。
trait Bar {
val name: String
val size: Int = name.size
}
object Foo extends {
val name = "Foo"
} with Bar
Scala 3 編譯器產生兩個錯誤訊息
-- Error: src/main/scala/early-initializer.scala:6:19
6 |object Foo extends {
| ^
| `extends` must be followed by at least one parent
-- [E009] Syntax Error: src/main/scala/early-initializer.scala:8:2
8 |} with Bar
| ^^^^
| Early definitions are not supported; use trait parameters instead
它建議使用特質參數,這將提供我們
trait Bar(name: String) {
val size: Int = name.size
}
object Foo extends Bar("Foo")
由於特質參數在 Scala 2.13 中不可用,因此無法跨編譯。如果您需要跨編譯的解決方案,可以使用一個中間類別,它將早期初始化的 val
和 var
作為建構函式參數。
早期初始化器在 Scala 2 中的另一個用例是子類別中的私有狀態,可透過超類別的建構函式(透過覆寫的方法)存取
class Adder {
var sum = 0
def add(x: Int): Unit = sum += x
add(1)
}
class LogAdder extends {
private var added: Set[Int] = Set.empty
} with Adder {
override def add(x: Int): Unit = { added += x; super.add(x) }
}
此案例可透過將私有狀態移至巢狀 object
來重構,此巢狀 object
會依需求初始化
存在類型
存在類型是 已移除的功能,這使得以下程式碼無效。
def foo: List[Class[T]] forSome { type T } // In Scala 3, Error: Existential types are no longer supported
存在類型是 Scala 2.13 中的實驗性功能,必須透過匯入
import scala.language.existentials
或設定-language:existentials
編譯器旗標來明確啟用。
在 Scala 3 中,建議的解決方案是引入一個封裝類型,它會承載依賴類型
請注意,使用萬用字元引數 _
或 ?
通常較簡單,但並非總是可行。例如,您可以用 List[?]
取代 List[T] forSome { type T }
。
專門化
Scala 2 中的 @specialized
注解在 Scala 3 中會被忽略。
但是,對於專門化的 Function
和 Tuple
有有限的支援。
可以從 inline
宣告中獲得類似的優點。