在 GitHub 上編輯此頁面

@targetName 註解

定義上的 @targetName 註解定義該定義實作的替代名稱。範例

import scala.annotation.targetName

object VecOps:
  extension [T](xs: Vec[T])
    @targetName("append")
    def ++= [T] (ys: Vec[T]): Vec[T] = ...

在此,++= 作業會以 append 名稱實作(在位元組碼或原生碼中)。實作名稱會影響所產生的程式碼,且為其他語言可呼叫方法的名稱。例如,++= 可像這樣從 Java 呼叫

VecOps.append(vec1, vec2)

@targetName 註解與 Scala 用法無關。在 Scala 中應用該方法必須使用 ++=,而非 append

詳細資料

  1. @targetName 定義在 scala.annotation 套件中。它會採用一個 String 類型的引數。該字串稱為已註解定義的「外部名稱」。

  2. @targetName 註解可提供給所有類型的定義,但頂層 classtraitobject 除外。

  3. @targetName 註解中給定的名稱必須是主機平台上已定義實體的合法名稱。

  4. 建議具有符號名稱的定義具有 @targetName 註解。這將建立一個較容易搜尋的替代名稱,並避免在執行時間診斷中出現難解的編碼。

  5. 名稱以反引號表示但不是合法主機平台名稱的定義也應該具有 @targetName 註解。

與覆寫的關係

@targetName 註解對於比對兩個方法定義以決定它們是否衝突或相互覆寫非常重要。如果兩個方法定義具有相同的名稱、簽章和擦除名稱,則它們會相符。在此,

  • 定義的簽章包含所有(值)參數和方法結果類型的擦除類型的名稱。
  • 方法定義的擦除名稱如果給定 @targetName 註解,則為其目標名稱,否則為其已定義名稱。

這表示可以透過 @targetName 註解來消除兩個原本會衝突的方法定義。例如。

def f(x: => String): Int = x.length
def f(x: => Int): Int = x + 1  // error: double definition

由於這兩個定義的擦除參數類型都是 Function0(這是按名稱參數轉換的類型),因此它們會衝突。因此,它們具有相同的名稱和簽章。但我們可以透過將 @targetName 註解新增至任一方法或兩個方法來避免衝突。範例

@targetName("f_string")
def f(x: => String): Int = x.length
def f(x: => Int): Int = x + 1  // OK

這將在產生的程式碼中產生方法 f_stringf

不過,@targetName 註解不得中斷具有相同名稱和類型的兩個定義之間的覆寫關係。因此,下列內容會出錯

import annotation.targetName
class A:
  def f(): Int = 1
class B extends A:
  @targetName("g") def f(): Int = 2

編譯器在此報告

-- Error: test.scala:6:23 ------------------------------------------------------
6 |  @targetName("g") def f(): Int = 2
  |                       ^
  |error overriding method f in class A of type (): Int;
  |  method f of type (): Int should not have a @targetName
  |  annotation since the overridden member hasn't one either

相關的覆寫規則可以總結如下

  • 如果兩個成員的名稱和簽章相同,而且它們具有相同的抹除名稱或相同的類型,則它們可以互相覆寫。
  • 如果兩個成員覆寫,則它們的抹除名稱和類型都必須相同。

與往常一樣,產生的程式碼中的任何覆寫關係也必須存在於原始程式碼中。因此,以下範例也會是一個錯誤

import annotation.targetName
class A:
  def f(): Int = 1
class B extends A:
  @targetName("f") def g(): Int = 2

在此,原始方法 gf 沒有互相覆寫,因為它們有不同的名稱。但一旦我們切換到目標名稱,就會發生編譯器報告的衝突

-- [E120] Naming Error: test.scala:4:6 -----------------------------------------
4 |class B extends A:
  |      ^
  |      Name clash between defined and inherited member:
  |      def f(): Int in class A at line 3 and
  |      def g(): Int in class B at line 5
  |      have the same name and type after erasure.
1 error found