完全限定名と正規名とバイナリ名

Java やってたらたまに遭遇する以下３つの名前について整理する。

- 完全限定名 (fully qualified name)
- 正規名 (canonical name)
- バイナリ名 (binary name)

※説明資料によっては「完全修飾名」「完全指定名」「正準名」のように翻訳がバラついているが、ここでは上記表記で統一する。

# 環境
**OS**

```
Windows 10
```

**Java**

```
> java --version
openjdk 11.0.8 2020-07-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.8+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.8+10, mixed mode)
```

# 定義
Java 言語仕様(Java Language Specification) に書いてある、それぞれの名前の定義を確認する。

## 完全限定名
[Chapter 6. Names](https://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.7)

> Every primitive type, named package, top level class, and top level interface has a fully qualified name:
> 
> - The fully qualified name of a primitive type is the keyword for that primitive type, namely byte, short, char, int, long, float, double, or boolean.
> - The fully qualified name of a named package that is not a subpackage of a named package is its simple name.
> - The fully qualified name of a named package that is a subpackage of another named package consists of the fully qualified name of the containing package, followed by ".", followed by the simple (member) name of the subpackage.
> - The fully qualified name of a top level class or top level interface that is declared in an unnamed package is the simple name of the class or interface.
> - The fully qualified name of a top level class or top level interface that is declared in a named package consists of the fully qualified name of the package, followed by ".", followed by the simple name of the class or interface.
> 
> Each member class, member interface, and array type may have a fully qualified name:
> 
> - A member class or member interface M **of another class or interface C** has a fully qualified name if and only if C has a fully qualified name.  
>   In that case, the fully qualified name of M consists of the fully qualified name of C, followed by ".", followed by the simple name of M.
> - An array type has a fully qualified name if and only if its element type has a fully qualified name.  
>   In that case, the fully qualified name of an array type consists of the fully qualified name of the component type of the array type followed by "[]".
> 
> A local class or anonymous class does not have a fully qualified name.

**【翻訳】**

全てのプリミティブ型、名前付きパッケージ、トップレベルクラス、そしてトップレベルインタフェースは、完全限定名（**fully qualified name**）を持つ。

- プリミティブ型の完全限定名は、そのプリミティブ型のキーワードとなる。  
  すなわち、 `byte`, `short`, `char`, `int`, `long`, `float`, `double`, `boolean`。
- 名前付きパッケージのサブパッケージ**ではない**名前付きパッケージの完全限定名は、そのパッケージの単純名となる
- 別の名前付きパッケージのサブパッケージである名前付きパッケージの完全限定名は、含まれているパッケージの完全限定名に `.` とそのパッケージの単純名を続けたものになる
- 無名パッケージ内で宣言されているトップレベルのクラスおよびインタフェースの完全限定名は、そのクラスまたはインタフェースの単純名になる
- 名前付きパッケージ内で宣言されているトップレベルのクラスおよびインタフェースの完全限定名は、含まれているパッケージの完全限定名に `.` と、そのクラスまたはインタフェースの単純名を続けたものになる

メンバークラス、メンバーインタフェース、配列は、完全限定名を持つ可能性がある。

- **別のクラスまたはインタフェース `C` の(of)**メンバークラスまたはインタフェース `M` は、 `C` が完全限定名を持つ場合に限り、完全限定名を持つ。  
  この場合、 `M` の完全限定名は、 `C` の完全限定名に `.` と `M` の単純名を続けたものになる。
- 配列は、配列の要素の型が完全限定名を持つ場合に限り、完全限定名を持つことができる。  
  この場合、配列の完全限定名は、配列のコンポーネント型の完全限定名の後ろに `[]` を続けたものになる。

ローカルクラスまたは匿名クラスは、完全限定名を持つことができない。


## 正規名
[Chapter 6. Names](https://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.7)
※完全限定名のすぐ後ろに正規名の説明がある

> Every primitive type, named package, top level class, and top level interface has a canonical name:
> 
> - For every primitive type, named package, top level class, and top level interface, the canonical name is the same as the fully qualified name.
> 
> Each member class, member interface, and array type may have a canonical name:
> 
> - A member class or member interface M **declared in another class or interface C** has a canonical name if and only if C has a canonical name.  
> In that case, the canonical name of M consists of the canonical name of C, followed by ".", followed by the simple name of M.
> - An array type has a canonical name if and only if its component type has a canonical name.  
> In that case, the canonical name of the array type consists of the canonical name of the component type of the array type followed by "[]".
> 
> A local class or anonymous class does not have a canonical name.

**【翻訳】**

全てのプリミティブ型、名前付きパッケージ、トップレベルのクラス、そしてトップレベルのインタフェースは、正規名(**canonical name**)を持つ。

- プリミティブ型、名前付きパッケージ、トップレベルのクラス、トップレベルのインタフェースは、正規名と完全限定名が等しい

メンバークラス、メンバーインタフェース、配列は、正規名を持つ可能性がある。

- **別のクラスまたはインタフェース `C` で宣言された(declared in)**メンバークラスまたはインタフェース `M` は、 `C` が正規名を持つ場合に限り正規名を持つ。  
  この場合、 `M` の正規名は `C` の正規名の後ろに `.` と `M` の単純名を続けたものになる。
- 配列は、コンポーネント型が正規名を持つ場合に限り、正規名を持つ。  
  この場合、配列の正規名はコンポーネント型の正規名の後ろに `[]` を続けたものになる。

ローカルクラスまたは匿名クラスは、正規名を持たない。

## バイナリ名
[Chapter 13. Binary Compatibility](https://docs.oracle.com/javase/specs/jls/se11/html/jls-13.html#jls-13.1)

> The class or interface must be named by its binary name, which must meet the following constraints:
> 
> - The binary name of a top level type (§7.6) is its canonical name (§6.7).
> - The binary name of a member type (§8.5, §9.5) consists of the binary name of its immediately enclosing type, followed by $, followed by the simple name of the member.
> - The binary name of a local class (§14.3) consists of the binary name of its immediately enclosing type, followed by $, followed by a non-empty sequence of digits, followed by the simple name of the local class.
> - The binary name of an anonymous class (§15.9.5) consists of the binary name of its immediately enclosing type, followed by $, followed by a non-empty sequence of digits.

※型変数の話は省略

**【翻訳】**

クラスまたはインタフェースは、次の制約を満たしたバイナリ名(**binary name**)で名付けられていなければならない。

- トップレベル型のバイナリ名は、正規名となる
- メンバー型のバイナリ名は、それを直接囲っている型のバイナリ名の後ろに `$` と、そのメンバー型の単純名をつなげたものになる
- ローカルクラスのバイナリ名は、それを直接囲っている型のバイナリ名の後ろに `$` と、空ではない数字の連番、ローカルクラスの単純名をつなげたものになる
- 匿名クラスのバイナリ名は、それを直接囲っている型のバイナリ名の後ろに `$` と、空ではない数字の連番をつなげたものになる

# 要するに
## 完全限定名
- プリミティブ型の完全限定名は、キーワード名と同じ
- 無名パッケージのトップレベルクラス・トップレベルインタフェース・名前付きパッケージ
    - `<そのクラス・インタフェース・パッケージの単純名>`
- 名前付きパッケージのトップレベルクラス・トップレベルインタフェース・名前付きパッケージ
    - `<所属するパッケージの完全限定名>.<そのクラス・インタフェース・パッケージの単純名>`
- メンバークラス・メンバーインタフェース
    - メンバーを**持つ**クラス・インタフェースが完全限定名を持つ
        - `<メンバーを持つクラス・インタフェースの完全限定名>.<メンバークラス・インタフェースの単純名>`
    - メンバーを**持つ**クラス・インタフェースが完全限定名を持たない
        - 完全限定名無し
- 配列
    - 要素の型が完全限定名を持つ
        - `<要素の型の完全限定名>[]`
    - 要素の型が完全限定名を持たない
        - 完全限定名無し
- 匿名クラス・ローカルクラス
    - 完全限定名無し

## 正規名
- プリミティブ型、名前付きパッケージ、トップレベルクラス、トップレベルインタフェース
    - 完全限定名と同じ
- メンバークラス・メンバーインタフェース
    - メンバーを**宣言している**クラス・インタフェースが正規名を持つ
        - `<メンバーを宣言しているクラス・インタフェースの正規名>.<メンバークラス・インタフェースの単純名>`
    - メンバーを**宣言している**クラス・インタフェースが正規名を持たない
        - 正規名無し
- 配列
    - 要素の型が正規名を持つ
        - `<要素の型の正規名>[]`
    - 要素の型が正規名を持たない
        - 正規名無し
- 匿名クラス・ローカルクラス
    - 正規名無し


## バイナリ名
- トップレベルの型
    - 正規名
- メンバー型
    - `<メンバーを直接囲っている型のバイナリ名>$<メンバーの単純名>`
- ローカルクラス
    - `<ローカルクラスを直接囲っている型のバイナリ名>$<連番><ローカルクラスの単純名>`
- 匿名クラス
    - `<匿名クラスを直接囲っている型のバイナリ名>$<連番>`


# 例
```java:TopLevelClass
package subpackage;

public class TopLevelClass {
    
    public void method() {
        class LocalClass {}
        Object anonymousClass = new Object() {};
    }
    
    public static class MemberClass {}
}
```

|型 |完全限定名 |正規名 |バイナリ名 |
|---|---|---|---|
|`int`<br>(プリミティブ型)|`int`|`int`|-|
|`String[]`<br>(配列)|`java.lang.String[]`|`java.lang.String[]`|-|
|`TopLevelClass`<br>(トップレベルのクラス)|`subpackage.TopLevelClass`|`subpackage.TopLevelClass`|`subpackage.TopLevelClass`|
|`MemberClass`<br>(メンバークラス)|`subpackage.TopLevelClass.MemberClass`|`subpackage.TopLevelClass.MemberClass`|`subpackage.TopLevelClass$MemberClass`|
|`LocalClass`<br>(ローカルクラス)|-|-|`subpackage.TopLevelClass$1LocalClass`|
|`anonymousClass`<br>(匿名クラス)|-|-|`subpackage.TopLevelClass$1`|

# 完全限定名と正規名の違い
パッと見だと、完全限定名と正規名の違いがわからない。
違いがあるのはメンバークラス・インタフェースのところで、仕様上の説明が若干異なっている。

> **完全限定名の説明**
> 
> - A member class or member interface M **of** another class or interface C has a fully qualified name if and only if C has a fully qualified name.  
>
> **正規名の説明**
> 
> - A member class or member interface M **declared in** another class or interface C has a canonical name if and only if C has a canonical name.  

完全限定名が `of` で、正規名が `declared in` となっている。

この違いが分かる例として、仕様書上は次のような解説コードが載っている。

```java
package p;
class O1 { class I {} }
class O2 extends O1 {}
```

この場合、メンバークラス `I` の完全限定名と正規名は次のようになる。

- 完全限定名
    - `p.O1.I` または `p.O2.I`
- 正規名
    - `p.O1.I` のみ

`I` を **宣言している(declared in)** のは `p.O1` なので、 `I` の正規名は `p.O1.I` だけに絞られる。
一方で、 `I` は `p.O1` を継承している `p.O2` **のもの(of)** でもあるので、完全限定名は `p.O1.I` と `p.O2.I` の２つとなる。

# java.lang.Class から取得できる名前
## Class.getCanonicalName()
[Class.getCanonicalName()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/Class.html#getCanonicalName())

> 『Java言語仕様』の定義に従って、基本となるクラスの正規名を返します。 基本となるクラスが正規名を持たない場合(ローカル・クラスや匿名クラス、またはコンポーネント型が正規名を持たない配列の場合)、nullを返します。

説明の通り、そのクラスの正規名を取得できる。
仕様上正規名を持たないケースは `null` を返す点に注意が必要。

## Class.getName()
[Class.getName()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/Class.html#getName())

> 『Java™言語仕様』で規定されているように、このクラス・オブジェクトが**配列型ではない参照型を表す場合は、クラスのバイナリ名**が返されます。
> 
> このクラス・オブジェクトが**プリミティブ型またはvoidを表す場合、返される名前はプリミティブ型またはvoidに対応するJava言語キーワード**と等価なStringです。
> 
> このクラス・オブジェクトが**配列のクラスを表す場合、名前の内部形式は、配列の入れ子の深さを表す1つ以上の[文字、要素のタイプの名前**という順序で構成されます。 要素のタイプの名前のエンコーディングは、次のとおりです。

- プリミティブ型、 `void`
    - キーワードと同じ
- 配列でない参照型
    - バイナリ名
- 配列
    - `[<エンコーディング>`
    - エンコーディングの詳細は Javadoc を参照

配列でない参照型の場合は、バイナリ名が取得できるようになっている。
バイナリ名はトップレベル型であれば正規名と同じなので、プリミティブ型とトップレベル型の場合は実質正規名と同じ値を返すことになる。

# Class.forName() で渡す名前
[Class.forName()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/Class.html#forName(java.lang.String))

> パラメータ:
> className - 要求するクラスの完全指定の名前 (**the fully qualified name of the desired class.**)。

引数の説明では「完全指定の名前 (fully qualified name)」となっているので完全限定名のことを指しているように思える。

```java
package sandbox;

public class Main {

    public static void main(String[] args) {
        try {
            Class.forName("sandbox.Main.MemberClass");
            System.out.println("SUCCESS: sandbox.Main.MemberClass");
        } catch (ClassNotFoundException e) {
            System.out.println("FAILED: sandbox.Main.MemberClass");
        }
        
        try {
            Class.forName("sandbox.Main$MemberClass");
            System.out.println("SUCCESS: sandbox.Main$MemberClass");
        } catch (ClassNotFoundException e) {
            System.out.println("FAILED: sandbox.Main$MemberClass");
        }
    }
    
    public static class MemberClass {}
}
```

```text:実行結果
FAILED: sandbox.Main.MemberClass
SUCCESS: sandbox.Main$MemberClass
```

実際に動かしてみると、完全限定名を渡したらエラーになって、バイナリ名を渡したら成功している。。。

よーく Javadoc を眺めてみると、 `toString()` の説明に次のように書かれていることに気づいた。

[Class.toString()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/Class.html#toString())

> その文字列は、classまたはinterfaceなどの文字列、空白、**getNameが返す形式の完全指定クラス名**という順序で表現されます。
> 原文: The string representation is the string "class" or "interface", followed by a space, and then by **the fully qualified name of the class in the format returned by getName**.

つまり、 `Class` オブジェクトの Javadoc の文脈での `fully qualified name` は、「Java言語仕様」で定義された `fully qualified name` ではなく、 `getName()` メソッドが返す文字列のことを指している、ということっぽい。


# 参考
- [The Java® Language Specification](https://docs.oracle.com/javase/specs/jls/se11/html/index.html)
- [概要 (Java SE 11 & JDK 11 )](https://docs.oracle.com/javase/jp/11/docs/api/index.html)
- [Javaパッケージ メモ(Hishidama's Java Package Memo)](https://www.ne.jp/asahi/hishidama/home/tech/java/package.html#h2_name)