此文件頁面專屬於 Scala 3,且可能涵蓋 Scala 2 中沒有的新概念。除非另有說明,否則此頁面中的所有程式碼範例都假設您使用的是 Scala 3。
內嵌
內嵌時請小心效能
若要充分利用 JVM JIT 最佳化,請避免產生大型方法。
巨集
即將推出
引號程式碼
保持引號可讀
- 盡量避免在內部使用任意表達式的
${...}
- 使用
$someExpr
- 使用
${ someExprFrom('localExpr) }
- 使用
舉例來說,請考慮下列範例
val sc: StringContext = ...
'{ StringContext(${Varargs(sc.parts.map(Expr(_)))}: _*) }
我們可以改寫成下列範例
val sc: StringContext = ...
val partExprs = sc.parts.map(Expr(_))
val partsExpr = Varargs(partExprs)
'{ StringContext($partsExpr: _*) }
在第二個範例中,引號的內容清晰許多。
避免巢狀內容
請考慮下列程式碼
val y: Expr[Int] = ...
def body(x: Expr[Int])(using quotes.Nested) = '{ $x + $y }
'{ (x: Int) => ${ body('x) } }
改用一般內容並傳遞所有需要的表達式。這也有好處,就是函式不必在本地定義。
def body(x: Expr[Int], y: Expr[Int])(using Quotes) =
'{ $x + $y }
val y: Expr[Int] = ...
'{ (x: Int) => ${ body('x, y) } }
引號反映
對於此區段,請考慮下列設定
object Box:
sealed trait Base
case class Leaf(x: Int) extends Base
// Quotes in contextual scope
val boxTpe : TypeRepr = TypeRepr.of[Box.type]
val baseTpe: TypeRepr = TypeRepr.of[Box.Base]
val baseSym: Symbol = baseTpe.typeSymbol
val leafTpe: TypeRepr = TypeRepr.of[Box.Leaf]
val leafSym: Symbol = leafTpe.typeSymbol
避免 Symbol.tree
在物件 sym: Symbol
上,sym.tree
會傳回與符號相關聯的 Tree
。使用此方法時請小心,因為符號的樹狀結構可能未定義。當與符號相關聯的程式碼在與此存取不同的時間定義時,如果未使用 -Yretain-trees
編譯選項,則符號的 tree
將無法使用。源自 Java 程式碼的符號沒有相關聯的 tree
。
從 Symbol
取得 TypeRepr
在先前的標題中,我們看到應避免使用 Symbol.tree
,因此不應在 sym: Symbol
上使用 sym.tree.tpe
。因此,建議在 tpe: TypeRepr
物件上使用 tpe.memberType
,以取得與 Symbol
對應的 TypeRepr
。
我們可以用兩種方式取得 Leaf
的 TypeRepr
TypeRepr.of[Box.Leaf]
boxTpe.memberType(leafSym)
(換句話說,我們要求Box
的成員TypeRepr
,其符號等於leafSym
的符號。)
雖然這兩種方法是等效的,但只有在您已經知道正在尋找類型 Box.Leaf
時,才能使用第一種方法。第二種方法允許您探索未知的 API。
使用 Symbol
比較定義
在此處 深入了解 符號。
符號允許您使用 ==
比較定義
leafSym == baseSym.children.head // Is true
然而,TypeRepr
上的 ==
不會產生相同的結果
boxTpe.memberType(baseSym.children.head) == leafTpe // Is false
取得類型的符號
有一個便捷的捷徑可以取得 T
定義的符號。取代
TypeTree.of[T].tpe.typeSymbol
您可以使用
TypeRepr.of[T].typeSymbol
模式比對進入 API
模式比對是使用 API 的一種非常符合人體工學的方法。務必查看 *Module
物件中定義的 unapply
方法。
在巨集中搜尋內容範圍
您可以使用 Implicits.search
搜尋特定執行個體。
例如
def summonOrFail[T: Type]: Expr[T] =
val tpe = TypeRepr.of[T]
Implicits.search(tpe) match
case success: ImplicitSearchSuccess =>
val implicitTerm = success.tree
implicitTerm.asExprOf[T]
case failure: ImplicitSearchFailure =>
reflect.report.throwError("Could not find an implicit for " + Type.show[T])
如果您正在撰寫巨集並偏好處理 Expr
,Expr.summon
是 Implicits.search
的一個方便包裝器。
def summonOrFail[T: Type]: Expr[T] =
Expr.summon[T] match
case Some(imp) => imp
case None => reflect.report.throwError("Could not find an implicit for " + Type.show[T])