レコード

Java®言語仕様バージョン15+36-1562への変更

このドキュメントでは、一貫性のあるクラスおよびインタフェースの用語ローカル静的インタフェースおよびenumクラスによる変更に伴う、Java SE 15のプレビュー機能であるレコードをサポートするためのJava言語仕様の変更点について説明します。この機能の概要については、「JEP 384」を参照してください。

変更点は、次のことを除き、Java SE 14のレコードの最初のプレビューと同じです。

比較ドキュメントは、レコードをサポートするためにJava仮想マシン仕様に必要な変更点を説明します。

これ以外のコンパニオン・ドキュメントでは、直列化可能レコードをサポートするためのJavaオブジェクト直列化仕様の変更点について説明します。

変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。

第1章: 概要

1.1 仕様の編成

...

第8章では、クラスについて説明します。クラスのメンバーは、クラス、インタフェース、フィールド(変数)およびメソッドです。クラス変数はクラスごとに1つ存在します。クラス・メソッドは、特定のオブジェクトを参照せずに動作します。インスタンス変数は、クラスのインスタンスであるオブジェクト内に動的に作成されます。インスタンス・メソッドは、クラスのインスタンス上で呼び出されます。このようなインスタンスは、実行時に現在のオブジェクトthisになり、オブジェクト指向のプログラミング・スタイルをサポートします。

クラスは単一継承をサポートしており、この場合、各クラスが単一のスーパークラスを持ちます。各クラスは、スーパークラスからメンバーを継承し、最終的にはクラスObjectから継承します。クラス型の変数は、そのクラスまたはそのクラスのサブクラスのインスタンスを参照できるため、多相的に既存のメソッドとともに新しい型を使用できます。

クラスは、synchronizedメソッドを使用した同時プログラミングをサポートします。メソッドは、実行によって発生する可能性があるチェック済例外を宣言します。これにより、コンパイル時のチェックが可能になり、例外条件を確実に処理できるようになります。オブジェクトは、オブジェクトがガベージ・コレクタによって破棄される前に呼び出されるfinalizeメソッドを宣言します。これにより、オブジェクトが状態をクリーン・アップできるようになります。

簡潔にするために、この言語には、クラスの実装とは別個の宣言「ヘッダー」や、別個の型およびクラス階層はありません。

特別な形式のクラスであるenumクラスは、小規模な値セットの定義およびその操作を型安全な方法でサポートします。enumクラスは、小規模な値セットの定義をサポートする特別な種類のクラスで、型安全な方法で使用できます。他の言語の列挙とは異なり、enum定数はオブジェクトであり、独自のメソッドを持つ場合があります。

レコード・クラスは、値の集計として機能する単純なオブジェクトの簡略式をサポートするもう1つの特別な種類のクラスです。

...

1.5 プレビュー機能

プレビュー機能の説明には、「レコード型」というテキストが追加されます。

レコード型に関連付けられた重要なAPI要素は、次のとおりです。

第3章: 字句構造

3.8 識別子

識別子は、Javaの文字およびJavaの数字からなる無限長のシーケンスで、先頭はJavaの文字です。

Identifier:
KeywordBooleanLiteralNullLiteralのいずれでもないIdentifierChars
IdentifierChars:
JavaLetter {JavaLetterOrDigit}
JavaLetter:
「Javaの文字」である任意のUnicode文字
JavaLetterOrDigit:
「Javaの文字または数字」である任意のUnicode文字

「Javaの文字」は、メソッドCharacter.isJavaIdentifierStart(int)がtrueを返す文字です。

「Javaの文字または数字」は、メソッドCharacter.isJavaIdentifierPart(int)がtrueを返す文字です。

「Javaの文字」には、大文字および小文字のASCIIのラテン文字A-Z (\u0041-\u005a)、a-z (\u0061-\u007a)、および歴史的理由によってASCIIのドル記号($または\u0024)とアンダースコア(_または\u005f)が含まれます。ドル記号は、機械的に生成されたソース・コードで使用するか、まれに、レガシー・システムで既存の名前にアクセスする場合のみ使用する必要があります。アンダースコアは、2つ以上の文字で構成された識別子には使用できますが、キーワードであるために1文字の識別子として使用することはできません。

「Javaの数字」には、ASCII数字0-9 (\u0030-\u0039)が含まれます。

文字および数字は、Unicode文字セット全体から取得できます。Unicode文字セットは、世界中で現在使用されているほとんどの作成スクリプトをサポートしています。これには、中国語、日本語および韓国語の大規模なセットが含まれます。これによりプログラマは、ネイティブ言語で作成されるプログラムで識別子を使用できるようになります。

識別子には、キーワード(3.9)、ブール・リテラル(3.10.3)またはnullリテラル(3.10.7)と同じスペル(Unicode文字列)を使用することはできません。使用すると、コンパイル時にエラーが発生します。

2つの識別子が同一であるのは、無視できるモジュールを無視した後に、識別子が文字または数字ごとに同じUnicode文字を持つ場合のみです。無視できる文字は、メソッドCharacter.isIdentifierIgnorable(int)がtrueを返す文字です。外観が同じである識別子でも、依然として異なる場合があります。

たとえば、単一文字LATIN CAPITAL LETTER A (A\u0041)、LATIN SMALL LETTER A (a\u0061)、GREEK CAPITAL LETTER ALPHA (A\u0391)、CYRILLIC SMALL LETTER A (a\u0430)およびMATHEMATICAL BOLD ITALIC SMALL A (a\ud835\udc82)で構成された識別子はすべて異なります。

Unicodeの複合文字は、正規の同等の分解文字とは異なります。たとえば、LATIN CAPITAL LETTER A ACUTE (Á\u00c1)は、識別子内でLATIN CAPITAL LETTER A (A, \u0041)の直後にNON-SPACING ACUTE (´\u0301)が付いたものとは異なります。『The Unicode Standard』のセクション3.11「Normalization Forms」を参照してください。

識別子の例は、次のとおりです。

識別子varおよびyieldおよびrecordは、一部のコンテキストでは許可されないため制限付き識別子です。

型識別子は、文字列varまたは文字列yieldでない識別子文字列varyieldおよびrecord以外の識別子です。

TypeIdentifier:
varまたはyieldまたはrecord以外の識別子

型識別子は、型の宣言または使用が含まれる特定のコンテキストで使用されます。たとえば、クラスの名前はTypeIdentifierである必要があるため、varまたはyieldまたはrecordという名前のクラスを宣言することは不正です(8.1)。

非修飾メソッド識別子は、文字列yieldでない識別子です。

UnqualifiedMethodIdentifier:
yield以外の識別子

この制限によって、yieldyield文(14.21)で使用でき、互換性の理由から(修飾)メソッド名としても使用できます。

3.9 キーワード

ASCII文字で構成された51文字の列は、キーワードとして使用するために予約されており、識別子として使用することはできません(3.8)。

キーワード:
(次のうちの1つ)
abstract continue for new switch
assert default if package synchronized
boolean do goto private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while
_ (アンダースコア)

キーワードconstおよびgotoは、現在使用されていませんが、予約されています。これにより、これらのC++のキーワードがプログラムで誤って出現した場合にJavaコンパイラがより適切なエラー・メッセージを作成できるようになります。キーワード_ (アンダースコア)は、将来パラメータ宣言で使用できるように予約されています。

場合によっては、様々な文字列が誤ってキーワードであるとみなされます。

これ以外の文字列として、openmodulerequirestransitiveexportsopenstousesprovidesおよびwithの10種類の制限付きキーワードがあります。これらの文字列は、ModuleDeclarationModuleDirectiveおよびRequiresModifierプロダクションの端末として表示される場合のみ、キーワードとしてトークン化されます(7.7)。これらは、それ以外のすべての場所で識別子としてトークン化されます。これにより、制限付きキーワードの導入前に作成されたプログラムとの互換性を確保します。例外が1つあります。文字列transitiveは、ModuleDirectiveプロダクション内の文字列requiresのすぐ右側で、後ろにセパレータが続かないかぎり、キーワードとしてトークン化されます。この場合、これは識別子としてトークン化されます。

第4章: 型、値および変数

4.11 型の使用場所

型は、ほとんどの種類の宣言内および特定の種類の式内で使用されます。特に、型を使用する1617型コンテキストがあります。

また、型は次としても使用されます。

最後に、Javaプログラミング言語には、型の使用方法を示す3つの特別な語句があります。

型コンテキストの型の意味は、次によって与えられます。

一部の型コンテキストは、参照型をパラメータ化する方法を制限します。

型が使用される任意の型コンテキストで、プリミティブ型を表すキーワード、または参照型の単純名を表すIdentifierに注釈を付けることができます。また、配列型内のネストの目的のレベルで[の左側に注釈を付けることにより、配列型に注釈を付けることもできます。これらの場所にある注釈は型注釈と呼ばれ、9.7.4に規定されています。いくつか例を挙げます。

宣言内に現れる5つ6つ型コンテキストは、宣言コンテキストと同じ数の構文上の場所を占有します(9.6.4.1)。

1つのプログラム内で同じ構文の場所が型コンテキストと宣言コンテキストの両方になる場合がありますが、これは、宣言の修飾子が宣言済エンティティの型の直前にくるためです。9.7.4では、このような場所内の注釈がどのように型コンテキストまたは宣言コンテキストあるいはその両方に現れると考えられるかについて説明します。

例4.11-1.型の使用方法

import java.util.Random;
import java.util.Collection;
import java.util.ArrayList;

class MiscMath<T extends Number> {
    int divisor;
    MiscMath(int divisor) { this.divisor = divisor; }
    float ratio(long l) {
        try {
            l /= divisor;
        } catch (Exception e) {
            if (e instanceof ArithmeticException)
                l = Long.MAX_VALUE;
            else
                l = 0;
        }
        return (float)l;
    }
    double gausser() {
        Random r = new Random();
        double[] val = new double[2];
        val[0] = r.nextGaussian();
        val[1] = r.nextGaussian();
        return (val[0] + val[1]) / 2;
    }
    Collection<Number> fromArray(Number[] na) {
        Collection<Number> cn = new ArrayList<Number>();
        for (Number n : na) cn.add(n);
        return cn;
    }
    <S> void loop(S s) { this.<S>loop(s); }  
}

この例では、型は次の宣言に使用されています。

および次の種類の式内:

第6章: 名前

6.1 宣言

宣言により、エンティティがプログラムに導入され、このエンティティを参照するために名前内で使用できる識別子(3.8)が組み込まれます。この識別子には、導入されるエンティティがクラス、インタフェースまたは型パラメータである場合は型識別子であるという制約があります。

宣言されたエンティティは、次のいずれかです。

コンストラクタ(8.8)も宣言によって導入されますが、新しい名前が導入されるのではなく、そのコンストラクタが宣言されたクラスの名前が使用されます。

...

6.5 名前の意味の確認

6.5.1 コンテキストに応じた名前の構文的分類

次のコンテキストでは、名前が構文的にModuleNameとして分類されます。

次のコンテキストでは、名前が構文的にPackageNameとして分類されます。

次のコンテキストでは、名前が構文的にTypeNameとして分類されます。

前述の16のコンテキスト内のReferenceTypeの識別子からのTypeNameの抽出は、要素型や型引数などのReferenceTypeのサブ用語すべてに繰り返し適用することを意図しています。

たとえば、フィールド宣言に型p.q.Foo[]が使用されるとします。配列型のカッコは無視され、用語p.q.Foo識別子の点線シーケンスとして配列型内のカッコの左側に抽出され、TypeNameとして分類されます。後のステップで、pqおよびFooのどれが型名またはパッケージ名であるかが確認されます。

別の例として、キャスト演算子に型p.q.Foo<? extends String>が使用されるとします。用語p.q.Foo識別子用語の点線シーケンスとして再度、今回はパラメータ化された型内の<の左側に抽出され、TypeNameとして分類されます。用語Stringが、パラメータ化された型のワイルドカード型引数のextends句内の識別子として抽出され、TypeNameとして分類されます。

次のコンテキストでは名前がExpressionNameとして構文的に分類されています。

次のコンテキストでは、名前が構文的にMethodNameとして分類されます。

次のコンテキストでは、名前が構文的にPackageOrTypeNameとして分類されます。

次のコンテキストでは、名前が構文的にAmbiguousNameとして分類されます。

構文的分類の結果、特定の種類のエンティティを式の特定の部分に制限できるようになります。

第7章: パッケージおよびモジュール

7.6 最上位クラスおよびインタフェースの宣言

最上位クラスまたはインタフェース宣言では、enumクラス(8.9)またはレコード・クラス(8.10)のいずれかである可能性がある最上位クラス(8.1)か、注釈インタフェース(9.6)である可能性がある最上位インタフェース(9.1)を宣言します。

TopLevelClassOrInterfaceDeclaration:
ClassOrInterfaceDeclaration
;
ClassOrInterfaceDeclaration:
ClassDeclaration
EnumDeclaration
RecordDeclaration
InterfaceDeclaration
AnnotationDeclaration

コンパイル・ユニットのクラス宣言またはインタフェース宣言のレベルで余分な;トークンが出現しても、コンパイル・ユニットの意味に影響を与えません。Javaプログラミング言語では、不要なセミコロンは、クラス宣言の後に;を配置することに慣れているC++プログラマのための便宜的措置としてのみ許容されています。新しいJavaコードでは使用しないでください。

アクセス修飾子がない場合、最上位クラスまたはインタフェースはパッケージ・アクセス権を持つため、宣言されたパッケージの通常コンパイル・ユニット内でのみアクセスできます(6.6.1)。クラスまたはインタフェースをpublicとして宣言して、同じモジュールの他のパッケージのコードから(および場合によっては他のモジュールのパッケージのコードから)クラスまたはインタフェースにアクセスすることもできます。

最上位クラスまたはインタフェースの宣言にprotectedprivateまたはstaticのアクセス修飾子のいずれかが含まれている場合は、コンパイル時にエラーが発生します。

最上位クラスまたはインタフェースの名前が、同じパッケージで宣言されている他の最上位クラスまたはインタフェースの名前として出現する場合は、コンパイル時にエラーが発生します。

最上位クラスまたはインタフェースのスコープおよびシャドウ化については、6.3および6.4に規定されています。

最上位クラスまたはインタフェースの完全修飾名については、6.7に規定されています。

例7.6-1.競合する最上位クラスまたはインタフェースの宣言

package test;
import java.util.Vector;
class Point {
    int x, y;
}
interface Point {  // compile-time error #1
    int getR();
    int getTheta();
}
class Vector { Point[] pts; }  // compile-time error #2

ここでは、最初のコンパイル時エラーの原因は、名前Pointを同じパッケージ内でクラスとインタフェースの両方として重複して宣言していることです。2番目のコンパイル時エラーは、クラス宣言と単一型インポート宣言の両方で名前Vectorを宣言しようとしたためです。

ただし、クラスの名前で、クラス宣言を含むコンパイル・ユニット(7.3)のtype-import-on-demand宣言(7.5.2)によってインポートされる可能性のあるクラスまたはインタフェースにも名前を付ける場合はエラーではないことに注意してください。つまり、次のプログラムでは、

package test;
import java.util.*;
class Vector {}  // not a compile-time error

クラスVectorの宣言は、クラスjava.util.Vectorも存在するとしても許可されます。このコンパイル・ユニット内では、単純名Vectorはクラスtest.Vectorを参照し、java.util.Vectorを参照するわけではありません(コンパイル・ユニット内のコードによってこれを参照することはできますが、完全修飾名によってのみ可能です)。

例7.6-2.最上位クラスおよびインタフェースのスコープ

package points;
class Point {
    int x, y;           // coordinates
    PointColor color;   // color of this point
    Point next;         // next point with this color
    static int nPoints;
}
class PointColor {
    Point first;        // first point with this color
    PointColor(int color) { this.color = color; }
    private int color;  // color components
}

このプログラムでは、クラス・メンバーの宣言で相互を使用する2つのクラスが定義されます。クラスPointおよびPointColorには、パッケージpoints (現在のコンパイル・ユニットのすべてのクラスを含む)のすべてのクラス宣言がスコープとして含まれているため、このプログラムは正常にコンパイルされます。つまり、前方参照は問題ではありません。

例7.6-3.完全修飾名

class Point { int x, y; }

このコードでは、package宣言がないコンパイル・ユニット内でクラスPointが宣言されているため、Pointはその完全修飾名です。一方、次のコードでは、

package vista;
class Point { int x, y; }

クラスPointの完全修飾名はvista.Pointです。(パッケージ名vistaは、ローカルまたは個人での使用には適しています。パッケージを広範に配布する予定がある場合は、一意のパッケージ名を付けることをお薦めします(6.1)。)

Java SEプラットフォームの実装では、パッケージ内のクラスおよびインタフェースを、それらを包含するモジュール名とバイナリ名の組合せによって記述する必要があります(13.1)。複数の方法で付けられたクラスまたはインタフェースの名前は、バイナリ名に展開して、そのような名前が同じクラスまたはインタフェースを参照していると確実に理解されるようにする必要があります。

たとえば、コンパイル・ユニットに次の単一型インポート宣言(7.5.1)が含まれているとします。

import java.util.Vector;

この場合、そのコンパイル・ユニット内で、単純名Vectorおよび完全修飾名java.util.Vectorは同じクラスを参照します。

パッケージがファイル・システムに格納されている場合(7.2)にのみ、ホスト・システムは、次のいずれかに該当し、かつ、クラスまたはインタフェースが、そのクラス名またはインタフェース名と拡張子(.java.javなど)で構成される名前のファイルに見つからない場合はコンパイル時にエラーが発生するという制限を強制することを選択できます。

この制限は、1つのコンパイル・ユニットについてそのようなクラスまたはインタフェースを1つまでにする必要があることを意味します。この制限によって、Javaコンパイラはパッケージ内で名前付きクラスまたはインタフェースを容易に見つけることができます。実際には、多くのプログラマは、各クラスまたはインタフェースがpublicであるかどうかや、他のコンパイル・ユニットのコードによって参照されているかどうかに関係なく、クラスまたはインタフェースをその独自のコンパイル・ユニットに配置します。

たとえば、publicwet.sprocket.Toadのソース・コードはディレクトリwet/sprocketのファイルToad.java内にあり、対応するオブジェクト・コードは同じディレクトリのファイルToad.class内にあります。

第8章: クラス

クラス宣言では、新しい参照型を定義し、これらの実装方法について記述します(8.1)。

最上位クラス(7.6)は、コンパイル・ユニットの最上位で宣言されるクラスです。

ネストしたクラスは、宣言が別のクラスまたはインタフェースの本体内で行われるクラスです。ネストしたクラスは、メンバー・クラス(8.59.5)、ローカル・クラス(14.3)または無名クラス(15.9.5)である場合があります。

内部クラス(8.1.3)は、包含クラス・インスタンス、ローカル変数および型変数を参照できる、ネストしたクラスです。

enumクラス(8.9)は、名前付きクラス・インスタンスの小規模なセットを定義する特別な構文で宣言されるクラスです。

レコード・クラス(8.10)は、値の単純な集計を定義する特別な構文で宣言されるクラスです。

この章では、すべてのクラスに共通するセマンティクスについて説明します。特定の種類のクラスに固有の詳細は、これらのコンストラクトに特化したセクションで説明します。

名前付きクラスはabstract (8.1.1.1)として宣言でき、これが不完全に実装されている場合は抽象であると宣言する必要があります。このようなクラスはインスタンス化できませんが、サブクラスによって拡張できます。クラスはfinal (8.1.1.2)として宣言される場合があります。この場合、このクラスはサブクラスを持つことができません。クラスがpublicとして宣言されている場合、そのモジュールの任意のパッケージ内のコードから、および場合によっては他のモジュール内のコードから参照できます。Objectを除く各クラスは、既存の単一のクラスの拡張(つまり、サブクラス) (8.1.4)であり、インタフェースを実装する場合があります(8.1.5)。クラスは汎用(8.1.2)である場合があります。つまり、クラスは、クラスの様々なインスタンス間でバインディングが異なる可能性のある型変数を宣言できます。

クラスは、他の種類の宣言と同じように、注釈(9.7)を使用して修飾できます。

クラスの本体は、メンバー(フィールド、メソッド、クラスおよびインタフェース)、インスタンスと静的イニシャライザ、およびコンストラクタ(8.1.6)を宣言します。メンバー(8.2)のスコープ(6.3)は、メンバーが属するクラスの宣言の本体全体です。フィールド、メソッド、メンバー・クラス、メンバー・インタフェースおよびコンストラクタ宣言には、アクセス修飾子(6.6) publicprotectedまたはprivateが含まれる場合があります。クラスのメンバーには、宣言されたメンバーと継承されたメンバーの両方が含まれます(8.2)。新しく宣言されたフィールドは、スーパークラスまたはスーパーインタフェース内で宣言されたフィールドを隠すことができます。新しく宣言されたメンバー・クラスおよびメンバー・インタフェースは、スーパークラスまたはスーパーインタフェース内で宣言されたメンバー・クラスおよびインタフェースを隠すことができます。新しく宣言されたメソッドは、スーパークラスまたはスーパーインタフェース内で宣言されたメソッドを隠す、実装する、またはオーバーライドすることができます。

フィールド宣言(8.3)では、1回インカネーションされるクラス変数、およびクラスのインスタンスごとに新しくインカネーションされるインスタンス変数を記述します。フィールドはfinal (8.3.1.2)として宣言できます。この場合、このフィールドは1回のみ割り当てることができます。任意のフィールド宣言にイニシャライザを含めることができます。

メンバー・クラス宣言(8.5)では、前後のクラスのメンバーであるネストしたクラスを記述します。メンバー・クラスはstaticであるか、内部クラスである場合があります(8.1.3)。

メンバー・インタフェース宣言(8.5)では、前後のクラスのメンバーであるネストしたインタフェースを記述します。

メソッド宣言(8.4)では、メソッド呼出し式(15.12)によって呼び出すことのできるコードを記述します。クラス・メソッドはクラスに関連して呼び出され、インスタンス・メソッドは、クラスのインスタンスである特定のオブジェクトに関連して呼び出されます。宣言が実装方法を示していないメソッドは、abstractとして宣言する必要があります。メソッドはfinal (8.4.3.3)として宣言される場合があります。この場合、このメソッドは隠したりオーバーライドすることができません。メソッドは、プラットフォーム依存のnativeコード(8.4.3.4)によって実装される場合があります。synchronizedメソッド(8.4.3.6)は、synchronized文(14.19)で使用されるように、本体を実行する前にオブジェクトを自動的にロックし、戻り時にオブジェクトを自動的にロック解除します。これにより、そのアクティビティを他のスレッド(17)のアクティビティと同期できるようになります。

メソッド名はオーバーロードする場合があります(8.4.9)。

インスタンス・イニシャライザ(8.6)は、作成時にインスタンスの初期化をサポートするために使用できる実行可能コードのブロックです(15.9)。

静的イニシャライザ(8.7)は、クラスの初期化をサポートするために使用できる実行可能コードのブロックです。

コンストラクタ(8.8)はメソッドと似ていますが、メソッド呼出しによって直接呼び出すことはできません。コンストラクタは、新しいクラス・インスタンスを初期化するために使用されます。メソッドと同様、これはオーバーロードする場合があります(8.8.8)。

8.1 クラス宣言

8.1.1 クラス修飾子

8.1.1.4 staticクラス

staticキーワードは、ネストされたクラスが内部クラスではないことを示しています(8.1.3)。クラスには直前と直後のインスタンスがなく、前後の型変数(6.5.5.1)、前後のインスタンス変数、ローカル変数、仮パラメータ、例外パラメータ(6.5.6.1)、また前後のインスタンス・メソッド(15.12.3)を直接参照できません。

ローカル・クラス宣言では、staticキーワードを使用できない場合があります(14.3)。

ネストしたenumクラスおよびレコード・クラスは、staticとして暗黙的に宣言されます。メンバーenumクラスまたはレコード・クラスstatic修飾子を重複して指定できますが、ローカルenumクラスまたはレコード・クラスは重複して指定できません(8.9)。

8.1.4 スーパークラスおよびサブクラス

標準クラス宣言内のオプションのextends句は、現在のクラスの直接スーパークラスを指定します。

スーパークラス:
extends ClassType

extends句は最初のクラスであり、直接スーパークラスを持たないため、クラスObjectの定義内に出現しないようにする必要があります。出現すると、コンパイル時にエラーが発生します。

ClassTypeは、アクセス可能なクラス型(6.6)に名前を付ける必要があります。そうでない場合、コンパイル時にエラーが発生します。

クラスfinalはサブクラスを持つことが許可されていないため、finalであるクラスにClassTypeが名前を付けた場合はコンパイル時にエラーが発生します(8.1.1.2)。

ClassTypeがクラスEnumの型またはEnumの呼出し(8.9)またはクラス型Recordに名前を付けた場合は、コンパイル時にエラーが発生します。

ClassTypeには型引数があります。これは、整形式のパラメータ化された型(4.5)を表し、いずれの型引数もワイルドカード型引数にすることはできません。ワイルドカード型引数にすると、コンパイル時にエラーが発生します。

(場合によっては汎用の)クラス宣言C<F1,...,Fn> (n 0, C Object)が与えられたときに、extends句が存在する場合、クラス型C<F1,...,Fn>直接スーパークラスは、Cの宣言のextends句で与えられた型です。そうでない場合はObjectです。

汎用クラス宣言C<F1,...,Fn> (n > 0)の場合、パラメータ化されたクラス型C<T1,...,Tn> (この場合Ti (1 i n)は型)の直接スーパークラスは、D<U1 θ,...,Uk θ> (この場合D<U1,...,Uk>C<F1,...,Fn>の直接スーパークラス、およびθ[F1:=T1,...,Fn:=Tn]の置換)です。

クラスは、直接スーパークラスの直接サブクラスであると言うことができます。直接スーパークラスは、その実装が現在のクラスの実装の導出元であるクラスです。

サブクラス関係は、直接サブクラス関係の推移閉包です。クラスAがクラスCのサブクラスであるのは、次のいずれかの条件が満たされる場合です。

クラスCがクラスAスーパークラスであると言えるのは、Aが常にCのサブクラスである場合です。

例8.1.4-1.直接スーパークラスおよびサブクラス

class Point { int x, y; }
final class ColoredPoint extends Point { int color; }
class Colored3DPoint extends ColoredPoint { int z; }  // error

この場合、これらの関係は次のとおりです。

クラスColored3dPointの宣言では、finalクラスColoredPointを拡張しようとするため、コンパイル時にエラーが発生します。

例8.1.4-2.スーパークラスおよびサブクラス

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
final class Colored3dPoint extends ColoredPoint { int z; }

この場合、これらの関係は次のとおりです。

クラスCが型T直接依存するのは、TCextendsまたはimplements句内で、スーパークラスまたはスーパーインタフェースとして、またはスーパークラスまたはスーパーインタフェース名の完全修飾形式の修飾子として指定されている場合です。

クラスCが参照型T依存するのは、次のいずれかの条件が満たされる場合です。

クラスがそれ自体に依存する場合は、コンパイル時にエラーが発生します。

クラスがロードされるときに、循環的に宣言されたクラスが実行時に検出された場合、ClassCircularityErrorがスローされます(12.2.1)。

例8.1.4-3.それ自体に依存するクラス

class Point extends ColoredPoint { int x, y; }
class ColoredPoint extends Point { int color; }

このプログラムを実行すると、クラスPointがそれ自体に依存しているため、コンパイル時にエラーが発生します。

8.5 メンバー・クラスおよびインタフェース宣言

メンバー・クラスは、宣言が別のクラスまたはインタフェース宣言の本体(8.1.69.1.4)に直接包含されているクラスです。メンバー・クラスはenumクラス(8.9)またはレコード・クラス(8.10)である場合があります。

メンバー・インタフェースは、宣言が別のクラスまたはインタフェース宣言の本体(8.1.69.1.4)に直接包含されているインタフェースです。メンバー・インタフェースは注釈インタフェース(9.6)である場合があります。

クラス内のメンバー・クラスまたはインタフェース宣言のアクセス可能性は、そのアクセス修飾子によって指定されるか、アクセス修飾子がない場合は6.6によって指定されます。

クラスが特定の名前のメンバー・クラスまたはインタフェースを宣言する場合、そのクラスまたはインタフェースの宣言は、クラスのスーパークラスおよびスーパーインタフェース内の同じ名前のメンバー・クラスおよびインタフェースのすべてのアクセス可能な宣言を隠すと言うことができます。

この観点では、メンバー・クラスおよびインタフェースを隠すことは、フィールドを隠すことに似ています(8.3)。

クラスは、その直接スーパークラスおよび直接スーパーインタフェースから、スーパークラスおよびスーパーインタフェースの非privateメンバー・クラスおよびインタフェースのうち、クラス内のコードにアクセス可能で、かつクラス内の宣言によって隠されないすべてのものを継承します。

クラスは、そのスーパークラスとスーパーインタフェース、またはそのスーパーインタフェースのみから、同じ名前の複数のメンバー・クラスまたはインタフェースを継承できます。そのような状況自体によってコンパイル時にエラーが発生することはありません。ただし、クラスの本体内でそのようなメンバー・クラスまたはインタフェースを単純名で参照しようとすると、参照があいまいなため、コンパイル時にエラーが発生します。

インタフェースから同じメンバー・クラスまたはインタフェースの宣言が継承されるパスが複数ある場合があります。このような状況下では、メンバー・クラスまたはインタフェースは1回のみ継承されたとみなされ、あいまいさなしに単純名で参照できます。

8.8 コンストラクタ宣言

コンストラクタは、クラスのインスタンスであるオブジェクトの作成時に使用されます(12.515.9)。

ConstructorDeclaration:
{ConstructorModifier} ConstructorDeclarator [Throws] ConstructorBody
ConstructorDeclarator:
[TypeParameters] SimpleTypeName
( [ReceiverParameter ,] [FormalParameterList] )
SimpleTypeName:
TypeIdentifier

このセクションのルールは、enum宣言およびレコード宣言を含むすべてのクラス宣言内のコンストラクタに適用されます。ただし、コンストラクタ修飾子、コンストラクタ本体およびデフォルト・コンストラクタに関してはenum宣言に特別なルールが適用されます。これらのルールについては、8.9.2を参照してください。特別なルールは、特別なコンパクト宣言形式を含め、コンストラクタに関するレコード宣言にも適用されます。詳細は、8.10.4を参照してください。

ConstructorDeclarator内のSimpleTypeNameは、コンストラクタ宣言を含むクラスの単純名である必要があります。そうでない場合、コンパイル時にエラーが発生します。

その他すべての点において、コンストラクタ宣言は、結果を持たないメソッド宣言に似ています(8.4.5)。

コンストラクタ宣言はメンバーではありません。これは決して継承されないため、隠されたりオーバーライドされたりすることはありません。

コンストラクタは、クラス・インスタンス作成式によって(15.9)、文字列連結演算子+を使用した変換および連結によって(15.18.1)、および他のコンストラクタからの明示的なコンストラクタ呼出しによって(8.8.7)呼び出されます。コンストラクタへのアクセスはアクセス修飾子によって制御されるため(6.6)、アクセス不可能なコンストラクタを宣言することによってクラスのインスタンス化を阻止できます(8.8.10)。

コンストラクタがメソッド呼出し式(15.12)によって呼び出されることはありません。

例8.8-1.コンストラクタ宣言

class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
}

8.10 レコード宣言

レコード宣言は、値の単純な集計を定義する特別な種類のクラスである新しいレコード・クラスを指定します。

RecordDeclaration:
{ClassModifier} record TypeIdentifier [TypeParameters] RecordHeader [SuperInterfaces] RecordBody

レコード宣言内のTypeIdentifierは、レコード・クラスの名前を指定します。

レコード宣言に修飾子abstractが含まれる場合は、コンパイル時にエラーが発生します。

レコード宣言は暗黙的にfinalです。レコード・クラスの宣言でfinal修飾子の重複指定が許可されています。

JEP 360は、シール・クラスをサポートするためにJavaを拡張することを提案しています。この場合、次のテキストが追加されます。

レコード宣言に修飾子sealedが含まれている場合は、コンパイル時にエラーが発生します。

ネストしたレコード宣言は暗黙的にstaticです。メンバー・レコード・クラスの宣言でstatic修飾子の重複指定が許可されています。ローカル・レコード宣言は、static修飾子を重複して指定できません(14.3)。

これは、内部クラスには定数変数以外のstaticメンバーを指定できないため、レコード・クラスを内部クラス(8.1.3)のメンバーとして宣言できないことを意味します。

レコード宣言に対して同じキーワードが修飾子として複数回出現する場合、またはレコード宣言にアクセス修飾子publicprotectedおよびprivateが複数含まれる場合(6.6)は、コンパイル時にエラーが発生します。

レコード・クラスの直接スーパークラスの型はRecordです。

レコード宣言にはextends句がないため、レコード・クラスの直接スーパークラスの型がRecordであることを明示的に宣言できません。

直列化メカニズムでは、レコード・クラスのインスタンスは通常の直列化可能オブジェクトまたは外部化可能オブジェクトとは異なる方法で扱われます。特に、レコード・オブジェクトは標準コンストラクタを使用して直列化復元されます(8.10.4)。

8.10.1 レコード・コンポーネント

レコード宣言のヘッダーはレコード・コンポーネント・リストで構成されます。これは、レコード・コンポーネントの空のリストである可能性があります。各レコード・コンポーネントは、型(オプションで、その前に1つ以上の注釈が付く)およびレコード・コンポーネントの名前を指定する識別子で構成されます。レコード・クラスにレコード・コンポーネントがない場合、レコード・ヘッダーは空のカッコのペアで構成されます。

各レコード・コンポーネントは、レコード・クラス(8.10.3)の暗黙的に宣言されたフィールドおよび(明示的または暗黙的に宣言された)アクセサ・メソッドに対応します。

RecordHeader:
( [ RecordComponentList ] )
RecordComponentList:
RecordComponent { , RecordComponent }
RecordComponent:
{ Annotation } UnannType Identifier
VariableArityRecordComponent
VariableArityRecordComponent:
{ Annotation } UnannType { Annotation } ... Identifier

レコード・コンポーネントは、型に続く省略記号が示す可変個引数レコード・コンポーネントである場合があります。1つのレコード型に対して最大で1つの可変個引数レコード・コンポーネントが許可されています。可変個引数レコード・コンポーネントがレコード・コンポーネントのリスト内で最後の位置以外の場所に出現する場合は、コンパイル時にエラーが発生します。

レコード宣言で同じ名前を持つ2つのレコード・コンポーネントを宣言する場合は、コンパイル時にエラーが発生します。

レコード宣言で名前clonefinalizegetClasshashCodenotifynotifyAlltoStringまたはwaitを持つレコード・コンポーネントを宣言する場合は、コンパイル時にエラーが発生します。

すべてのレコード・コンポーネントには対応するアクセサ・メソッドがある(8.10.3)ため、この制限により、レコード・クラスがクラスObjectのメソッドをオーバーライドまたはオーバーロードすることが防止されます。

レコード・コンポーネントに対する注釈修飾子のルールは、9.7.4に規定されています。レコード・クラスのレコード・コンポーネントの注釈は、8.10.2および8.10.3に規定されているとおり、レコード・クラスのメンバーおよびコンストラクタに伝播される場合があります。レコード・コンポーネントの注釈は、その注釈型がレコード・コンポーネントのコンテキストに適用できる場合にのみ、レコード・コンポーネントに残ります。

宣言されるレコード・コンポーネントの型は、それが可変個引数レコード・コンポーネントであるかどうかによって異なります。

宣言された可変個引数レコード・コンポーネントの型に型情報保持可能でない要素型(4.7)が含まれる場合に、標準コンストラクタ(8.10.4)に@SafeVarargs (9.6.4.7)の注釈が付けられていないか、または警告が@SuppressWarnings (9.6.4.5)によって抑制されていないと、コンパイル時に可変個引数レコード・コンポーネントの宣言に対して未チェック警告が発生します。

8.10.2 レコード本体

レコード宣言の本体には、コンストラクタおよびメンバー宣言の他に、静的イニシャライザが含まれる場合があります。

RecordBody:
{ {RecordBodyDeclaration} }
RecordBodyDeclaration:
ClassBodyDeclaration
CompactConstructorDeclaration

便宜上、ここでは8.1.6の次のプロダクションを示します。

ClassBodyDeclaration:
ClassMemberDeclaration
InstanceInitializer
StaticInitializer
ConstructorDeclaration
ClassMemberDeclaration:
FieldDeclaration
MethodDeclaration
ClassOrInterfaceDeclaration
;

レコード宣言の本体に非staticフィールド宣言(8.3.1.1)が含まれる場合は、コンパイル時にエラーが発生します。

レコード宣言の本体にnativeメソッド宣言(8.4.3.4)が含まれる場合は、コンパイル時にエラーが発生します。

レコード宣言の本体にインスタンス・イニシャライザ(8.6)が含まれる場合は、コンパイル時にエラーが発生します。

8.10.3 レコード・メンバー

レコード・コンポーネント・リストに出現する各レコード・コンポーネントのレコード・クラスには、レコード・コンポーネントと同じ名前かつ宣言されたレコード・コンポーネントの型と同じ型の、暗黙的に宣言されたフィールドがあります。このフィールドはprivateおよびfinalとして宣言されます。このフィールドに付けられる注釈は、対応するレコード・コンポーネント内に出現し、注釈型がフィールド宣言コンテキストまたは型コンテキスト、あるいはこれら両方に適用できる注釈です。

レコード宣言では、レコード・コンポーネントのアクセサ・メソッドは、指定されたレコード・コンポーネントの名前と同じ名前で、仮パラメータ・リストが空のメソッドです。

レコード・コンポーネントのアクセサ・メソッドを明示的に宣言するには、次の条件を満たす必要があります。

そうでない場合、コンパイル時にエラーが発生します。

レコード・コンポーネントのアクセサ・メソッドが明示的に宣言されない場合は、次の特性を持つアクセサ・メソッドが暗黙的に宣言されます。

したがって、レコード・クラスには、レコード・コンポーネント・リストに出現するすべてのレコード・コンポーネントのアクセサ・メソッドが含まれます。

レコード・コンポーネントに出現する注釈は、そのレコード・コンポーネントに対して明示的に宣言されたアクセサ・メソッドには伝播されません。これは、対応するレコード・コンポーネントの適用可能な注釈を使用して注釈が付けられる、暗黙的に宣言されたアクセサ・メソッドとは対照的です。

アクセサ・メソッド(明示的または暗黙的)は、レコード・クラスのスーパーインタフェースで宣言されたメソッドをオーバーライドまたはオーバーロードする場合があります。

コンポーネント名(8.10.1)に対する制限は、アクセサ・メソッドにクラスObjectの非privateメソッドとオーバーライド等価であるシグネチャがないことを意味します。

すべてのレコード・クラスは、クラスRecordで宣言されたabstractメソッドの実装を提供します。次の各メソッドでは、レコード・クラスが同じシグネチャ(8.4.2)を持つメソッドを明示的に宣言しない場合、メソッドは暗黙的に宣言されます。

8.2で説明されているとおり、レコード・クラスは他のメンバーを宣言または継承する場合があります。レコード・クラスのすべてのメンバー(暗黙的に宣言されたメンバーを含む)は、クラス内のメンバー宣言に関する通常のルール(8.38.48.5)の対象になります。

たとえば、レコード・クラスは、その直接スーパーインタフェースからデフォルトのメソッドを継承できます。このように宣言するとします。

interface Logging{
    default void logAction() { ... }
}

record Point(int i, int j) implements Logging {}

これにより、次のコードが想定どおりに機能します。

Point p = new Point(42,37);
p.logAction();

8.10.4 レコード・コンストラクタ宣言

レコード・クラスは、そのレコード・コンポーネントの適切な初期化をサポートするために、デフォルトのコンストラクタ(8.8.9)を暗黙的に宣言しません。かわりに、レコード・クラスには、レコード・コンポーネントに対応するすべてのフィールドを初期化する、明示的または暗黙的に宣言された標準コンストラクタがあります。

レコード・クラスRには導出されたコンストラクタ・シグネチャがあります。これは、名前R、および宣言された各レコード・コンポーネントの型を取得することによってRのレコード・コンポーネント・リストから導出された仮パラメータ型で構成され、型パラメータはありません。

レコード・クラスRには導出された仮パラメータ・リストがあります。これは、レコード・コンポーネント・リストの各レコード・コンポーネントを取得し、レコード・コンポーネントと同じ名前および宣言された型を持つ仮パラメータを導出することによって構築されます。

レコード宣言で標準コンストラクタを明示的に宣言するには、特定のシグネチャを使用してコンストラクタを宣言する方法と、コンパクト・コンストラクタを宣言する方法の2通りがあります。

レコード・クラスRの宣言内のコンストラクタは、そのシグネチャがRの導出されたコンストラクタ・シグネチャとオーバーライド等価(8.4.2)である場合、標準コンストラクタと言うことができます。

標準コンストラクタはレコード・クラスの導出されたコンストラクタ・シグネチャとオーバーライド等価であるシグネチャを持つため、明示的に宣言された標準コンストラクタは1つのみ存在できます。

レコード・クラスの標準コンストラクタのアクセス修飾子は、次のように、少なくともレコード・クラスと同程度のアクセスを提供する必要があります。

コンパクト・コンストラクタではなく、標準コンストラクタであるコンストラクタ宣言は、次の条件を満たす必要があります。

そうでない場合、コンパイル時にエラーが発生します。

これらのルールにより、レコード・コンポーネントの注釈が、明示的に宣言されたコンストラクタの対応する仮パラメータの注釈とは異なる場合があります。たとえば、次が有効です。

record R(@DevAnnotation("devA") String s) {
  R(@DevAnnotation("devB") String s) {
    // Explicitly declared canonical constructor
    ...
  }
}

レコード宣言で標準コンストラクタを明示的に宣言する2つ目の方法では、コンパクト・コンストラクタ宣言を指定します。これは、レコード宣言でのみ使用できるコンストラクタ宣言の特別で簡潔な形式です。

CompactConstructorDeclaration:
{ Annotation } { ConstructorModifier } SimpleTypeName ConstructorBody

レコード・クラスRでは、コンパクト・コンストラクタ宣言の仮パラメータ・リストは暗黙的に宣言され、導出されたRの仮パラメータ・リストによって指定されます。

したがって、cという名前のレコード・コンポーネントを含むレコード宣言およびコンパクト・コンストラクタ宣言の場合、コンパクト・コンストラクタの本体内の非修飾名cの出現は、暗黙の仮パラメータcを示します。

レコード・クラスRでは、コンパクト・コンストラクタ宣言のシグネチャは、導出されたRのコンストラクタ・シグネチャです。

レコード・クラスで複数のコンパクト・コンストラクタを宣言すると、コンパイル時にエラーが発生します。

クラス宣言(8.8.2)内のコンストラクタのシグネチャに関するルールに基づき、レコード宣言にコンパクト・コンストラクタ宣言、および標準コンストラクタである標準コンストラクタ宣言が含まれている場合も、コンパイル時にエラーが発生します。コンパクト・コンストラクタは標準コンストラクタです。

コンパクト・コンストラクタ宣言は、次の条件をすべて満たす必要があります。そうでない場合、コンパイル時にエラーが発生します。

コンパクト・コンストラクタの本体内のレコード・クラスのレコード・コンポーネントに対応するフィールドへの割当てが出現する場合(16)、コンパイル時にエラーが発生します。

レコード・クラスのレコード・コンポーネントに対応するすべてのフィールドは、コンパクト・コンストラクタの本体の後の、対応する仮パラメータの値に暗黙的に初期化されます。これらのフィールドは、レコード・コンポーネント・リスト内で宣言される順序で暗黙的に初期化されます。

コンパクト・コンストラクタ宣言は、検証および/または正規化コードのみをコンストラクタ本体に提供することを意図しています。残りの初期化コードはコンパイラによって提供されます。次に、その単純な例を示します。

record Rational(int num, int denom) { 
    Rational {
        int gcd = gcd(num, denom);
        num /= gcd;
        denom /= gcd;
    }
}

この宣言は次の宣言と同等です。

record Rational(int num, int denom) { 
    Rational(int num, int demon) {
        int gcd = gcd(num, denom);
        num /= gcd;
        denom /= gcd;
        this.num = num;
        this.denom = denom;
    }
}

レコード・クラスRの宣言で、標準コンストラクタが明示的に宣言されない場合は、次の特性を持つ標準コンストラクタが暗黙的に宣言されます。

レコード・クラスRの宣言に、標準コンストラクタでないコンストラクタの他の宣言がある場合、標準クラス宣言(8.8)内のコンストラクタ宣言のルールを満たすことに加えて、そのような各コンストラクタ(存在する場合)は次の条件を満たす必要があります。

そうでない場合、コンパイル時にエラーが発生します。

第9章 インタフェース

9.6 注釈インタフェース

9.6.4 事前定義済注釈インタフェース

9.6.4.1 @Target

java.lang.annotation.Targetの注釈を注釈インタフェースTの宣言で使用して、T適用可能であるコンテキストを指定します。java.lang.annotation.Targetには、コンテキストを指定するための型java.lang.annotation.ElementType[]の単一要素valueが含まれます。

注釈インタフェースは、宣言に注釈が適用される宣言コンテキスト内、または宣言および式で使用される型に注釈が適用される型コンテキスト内に適用可能である場合があります。

9つ10の宣言コンテキストがあり、各コンテキストがjava.lang.annotation.ElementTypeのenum定数に対応しています。

  1. モジュール宣言(7.7)

    java.lang.annotation.ElementType.MODULEに対応しています

  2. パッケージ宣言(7.4.1)

    java.lang.annotation.ElementType.PACKAGEに対応しています

  3. 型宣言: クラス、インタフェース、enum、レコード、および注釈宣言(8.1.19.1.18.59.58.98.109.6)

    java.lang.annotation.ElementType.TYPEに対応しています

    また、注釈宣言はjava.lang.annotation.ElementType.ANNOTATION_TYPEに対応しています

  4. メソッド宣言(注釈インタフェースの要素を含む) (8.4.39.49.6.1)

    java.lang.annotation.ElementType.METHODに対応しています

  5. コンストラクタ宣言(8.8.3)

    java.lang.annotation.ElementType.CONSTRUCTORに対応しています

  6. 汎用クラス、インタフェース、メソッドおよびコンストラクタの型パラメータ宣言(8.1.29.1.28.4.48.8.4)

    java.lang.annotation.ElementType.TYPE_PARAMETERに対応しています

  7. フィールド宣言(enum定数を含む) (8.3.19.38.9.1)

    java.lang.annotation.ElementType.FIELDに対応しています

  8. 仮パラメータおよび例外パラメータ宣言(8.4.19.414.20)

    java.lang.annotation.ElementType.PARAMETERに対応しています

  9. ローカル変数宣言(for文のループ変数およびtry-with-resources文のリソース変数を含む) (14.414.14.114.14.214.20.3)

    java.lang.annotation.ElementType.LOCAL_VARIABLEに対応しています

  10. レコード・コンポーネント宣言(8.10.1)

    java.lang.annotation.ElementType.RECORD_COMPONENTに対応しています

1617の型コンテキスト(4.11)があり、これらはすべて、java.lang.annotation.ElementTypeのenum定数TYPE_USEによって表されます。

java.lang.annotation.Targetの注釈のvalue要素内に同じenum定数が複数回出現すると、コンパイル時にエラーが発生します。

java.lang.annotation.Targetの注釈が注釈インタフェースTの宣言内に存在しない場合、T910のすべての宣言コンテキストおよび1617のすべての型コンテキストに適用できます。

9.6.4.4 @Override

プログラマは、メソッド宣言をオーバーライドするときにそれをオーバーロードすることがあり、微妙な問題につながります。注釈インタフェースOverrideは、そのような問題の早期検出をサポートします。

従来の例は、equalsメソッドに関するものです。プログラマは、クラスFooで次のように記述します。

public boolean equals(Foo that) { ... }

実際に記述しようとした内容は次のとおりです。

public boolean equals(Object that) { ... }

これは完全に正当ですが、クラスFooObjectからequals実装を継承するため、微妙なバグの原因となる可能性があります。

クラスまたはインタフェースTのメソッド宣言に@Overrideの注釈が付けられているが、メソッドがTからTのスーパー・タイプ(8.4.8.19.4.1.1)で宣言されたメソッドをオーバーライドしない場合、またはObjectpublicメソッド(4.3.28.4.2)とオーバーライド等価でない場合、コンパイル時にエラーが発生します。

次の条件のいずれかが満たされない場合に、クラスまたはインタフェースTのメソッド宣言に@Overrideの注釈が付けられると、コンパイル時にエラーが発生します。

この動作は、Java SE 5.0とは異なります。Java SE 5.0では、@Overrideによってコンパイル時にエラーが発生するのは、スーパーインタフェースからメソッドを実装し、スーパークラスにも存在しないメソッドに適用された場合のみでした。

publicメソッドのオーバーライドに関する条項の背景には、インタフェースでの@Overrideの使用があります。次の宣言を考えてみましょう。

class Foo     { @Override public int hashCode() {..} }
interface Bar { @Override int hashCode(); }

Foo.hashCodeはメソッドObject.hashCodeFooからオーバーライドするため、クラス宣言での@Overrideの使用は、最初の条項によって正当です。

インタフェース宣言については、インタフェースがObjectをスーパー・タイプとして持たず、Objectpublicメンバーに対応するpublic abstractメンバーをインタフェースが持つとします(9.2)。インタフェースでそれらを明示的に宣言する(つまり、Objectpublicメソッドとオーバーライド等価であるメンバーを宣言する)ことを選択した場合、インタフェースはそれらをオーバーライドするとみなされ、@Overrideの使用は許可されます。

一方、cloneメソッドで@Overrideを使用しようとするインタフェースを考えてみましょう。(この例では、finalizeを使用することもできます)

interface Quux { @Override Object clone(); }

Object.clonepublicでないため、Quuxで暗黙的に宣言されたcloneというメンバーはありません。したがって、Quux内のcloneの明示的な宣言は他のメソッドを実装するとみなされず、@Overrideの使用は誤りです。(Quux.clonepublicであるという事実は関係ありません。)

対照的に、cloneを宣言するクラス宣言は単純にObject.cloneをオーバーライドするため、@Overrideを使用できます。

class Beep { @Override protected Object clone() {..} }

@Override注釈はレコード宣言で特別な意味を持ち、メソッド宣言がレコード・コンポーネントのアクセサ・メソッド(8.10.3)であることを指定するために使用できます。次の宣言を考えてみましょう。

record R(int x) {
   @Override
   public int x() { 
       return Math.abs(x);
   }
   ...
}

アクセサ・メソッドx@Override注釈により、レコード・コンポーネントxが変更または削除された場合は、対応するアクセサ・メソッドも変更または削除する必要があります。

9.7 注釈

9.7.4 注釈が出現する可能性がある場所

宣言注釈は、宣言に適用され、その宣言によって表される宣言コンテキスト(9.6.4.1)に独自の型を適用できる注釈、またはクラス、インタフェース、enum、レコード、注釈型または型パラメータ宣言に適用され、型コンテキスト(4.11)に独自の型を適用できる注釈です。

型注釈は、型(または型の任意の部分)に適用され、型コンテキストに独自の型を適用できる注釈です。

たとえば、次のフィールド宣言が与えられた場合、

@Foo int f;

@Fooは、Foo@Target(ElementType.FIELD)によってメタ注釈が付けられた場合はfに対する宣言注釈であり、Foo@Target(ElementType.TYPE_USE)によってメタ注釈が付けられた場合はintに対する型注釈です。@Fooは同時に宣言注釈と型注釈の両方にすることができます。

型注釈は、配列型またはそのコンポーネント型(10.1)に適用できます。たとえば、ABおよびC@Target(ElementType.TYPE_USE)によってメタ注釈が付けられた注釈型であるとするときに、次のフィールド宣言が与えられた場合、

@C int @A [] @B [] f;

@Aは配列型int[][]に適用され、@Bはコンポーネント型int[]に適用され、@Cは要素型intに適用されます。その他の例については、次の情報を参照してください 10.2

この構文の重要な特性は、2つの宣言においては配列レベルの数のみが異なり、型の左側注釈が同じ型を参照している点です。たとえば、@Cは、次のすべての宣言内の型intに適用されます。

@C int f;
@C int[] f;
@C int[][] f;

宣言注釈を他のすべての修飾子の前に書き、型注釈をそれが適用される型の直前に書くことは慣例ですが、必須ではありません。

プログラム構文で、一見すると宣言または型、あるいはその両方に適用されるような場所に注釈が出現する場合があります。これは、宣言されたエンティティの型の直前に修飾子がある次の5つ6つの宣言コンテキストのいずれかで考えられます。

Javaプログラミング言語の文法では、これらの場所にある注釈が宣言(8.3)の修飾子として明確に取り扱われますが、これは純粋に構文上だけのものです。注釈が宣言または宣言されたエンティティの型のどちらに適用されるか、つまり、注釈が宣言注釈または型注釈のどちらであるかは、注釈の型の適用性によって決まります。

前述の2番目のと3番目のケースでは、注釈に最も近い型は、次のように決定されます。

Tの注釈が次の構文上の修飾子である場合、コンパイル時にエラーが発生します。

これら911個の条項のうち56つで"...または型コンテキスト"と記載されているのは、これらが、一見すると宣言または宣言されたエンティティの型に注釈を適用できそうな、構文上の5つ6つの場所であるためです。また、クラス、インタフェース、enum、レコードおよび注釈型宣言、さらに型パラメータ宣言に関するこれら9つ11の条項のうち2つの条項で「~または型コンテキスト」と記載されているのは、型に@Target(ElementType.TYPE_USE)がメタ注釈として付けられている(ため、型コンテキストに適用可能である)注釈を型宣言に適用する方が便利である可能性があるからです。

次の両方が当てはまる場合、型注釈は許容されます。

2番目の条項では、Outerに囲まれたネストしたクラス内でOuter.thisが正当である場合、Outerは、実行時に一部のオブジェクトの型を表すため、注釈が付けられる可能性があると考えられます。その一方で、Outer.thisが正当ではない場合(これが出現するクラスには実行時にOuterの包含インスタンスがないため)、Outerは、論理的に単なる名前(完全修飾された型名のパッケージ名のコンポーネントと同種)であるため、注釈が付けられない可能性があります。

たとえば、次のプログラムの場合、Bには語彙的な包含インスタンスがないため(8.5.1)、Bの本体内にA.thisを作成することはできません。したがって、Aは論理的に型ではなく単なる名前であるため、@Fooを型A.B内のAに適用することはできません。

@Target(ElementType.TYPE_USE)
@interface Foo {}

class Test {
  class A {
    static class B {}
  }

  @Foo A.B x;  // Illegal 
}

その一方で、次のプログラムの場合、Dの本体内にC.thisを作成できます。したがって、Cは実行時の一部のオブジェクトの型を表すため、@Fooを型C.D内のCに適用できます。

@Target(ElementType.TYPE_USE)
@interface Foo {}

class Test {
  static class C {
    class D {}
  }

  @Foo C.D x;  // Legal 
}

最後に、2番目の条項は、修飾された型内で1レベル深く見えます。これは、staticクラスが最上位クラスまたは別のネストしたstaticクラス内でのみネストされる可能性があるからです。次のようなネストを作成することはできません。

@Target(ElementType.TYPE_USE)
@interface Foo {}

class Test {
  class E {
    class F {
      static class G {}
    }
  }

  @Foo E.F.G x;
}

ここでしばらくの間、このネストが正当ではなかったと仮定します。フィールドxの型で、Gの本体ではE.F.thisが不正であるため、EおよびFは論理的にGを修飾する名前です。この場合、@FooEの隣では正当になりません。ただし、技術的には、@Fooは次の深さにある語句Fが内部クラスを示すため、Eの隣にあることが許容されます。しかし、本来クラスのネストは不正であるため、これには議論の余地があります。

Tの注釈が型コンテキスト内の型の最も外側のレベルに適用されるときに、型コンテキスト、または同じ構文上の場所を占有する宣言コンテキスト(存在する場合)にTを適用できない場合、コンパイル時にエラーが発生します。

Tの注釈が型コンテキスト内の型の一部(つまり、最も外側のレベルではない)に適用されるときに、型コンテキストにTを適用できない場合、コンパイル時にエラーが発生します。

Tの注釈が型コンテキスト内の型(または型の一部)に適用されるときに、型コンテキストにTを適用でき、注釈が許容されない場合、コンパイル時にエラーが発生します。

たとえば、@Target(ElementType.TYPE_USE)によってのみメタ注釈が付けられた注釈型TAがあるとします。@TAが最も近い単純名がパッケージ名として分類されているため、語句@TA java.lang.Objectおよびjava.@TA lang.Objectは不正です。一方、java.lang.@TA Objectは不正です。

不正な語句は「どこでも」不正です。注釈パッケージ名に対する禁止事項は広範に適用されます。具体的には、単なる型コンテキストである場所(class ... extends @TA java.lang.Object {...}など)、および宣言コンテキストと型コンテキストの両方である場所(@TA java.lang.Object f;など)に適用されます。(クラス、パッケージおよび型パラメータ宣言では単純名のみが使用されるため、パッケージ名に注釈を付けられる単なる宣言コンテキストである場所は存在しません。)

TAに追加的に@Target(ElementType.FIELD)のメタ注釈が付けられた場合、語句@TA java.lang.Objectは、宣言コンテキストと型コンテキストの両方である場所(フィールド宣言@TA java.lang.Object f;など)で正当になります。ここでは、TAはフィールド宣言コンテキストに適用できるため、@TAは(型java.lang.Objectに対してではなく) fの宣言に対して適用されるとみなされます。

第10章: 配列

10.2 配列変数

配列型の変数には、オブジェクトへの参照が格納されます。配列型の変数を宣言しても、配列オブジェクトが作成されたり、配列コンポーネントにスペースが割り当てられることはありません。配列への参照を含めることのできる変数自体のみが作成されます。ただし、宣言子のイニシャライザ部分(8.39.314.4.1)によって配列が作成され、この配列に対する参照が変数の初期値になる場合があります。

例10.2-1.配列変数の宣言

int[]     ai;        // array of int
short[][] as;        // array of array of short
short     s,         // scalar short
          aas[][];   // array of array of short
Object[]  ao,        // array of Object
          otherAo;   // array of Object
Collection<?>[] ca;  // array of Collection of unknown type

前述の宣言では配列オブジェクトは作成されません。配列オブジェクトを作成する配列変数の宣言の例を次に示します。

Exception ae[]  = new Exception[3];
Object aao[][]  = new Exception[2][3];
int[] factorial = { 1, 1, 2, 6, 24, 120, 720, 5040 };
char ac[]       = { 'n', 'o', 't', ' ', 'a', ' ',
                    'S', 't', 'r', 'i', 'n', 'g' };
String[] aas    = { "array", "of", "String", };

変数の配列型は、変数宣言の先頭にある型の一部として、または変数の宣言子の一部として、あるいはその両方として出現する可能性があるカッコのペアによって異なります。特に、フィールド、仮パラメータ、またはローカル変数またはレコード・コンポーネント (8.38.4.19.39.414.4.114.14.215.27.18.10.1)の宣言では、変数の配列型は次によって示されます。

メソッドの戻り型(8.4.5)は配列型である場合があります。正確な配列型は、メソッド宣言の先頭にある型の一部として、またはメソッドの仮パラメータ・リストの後ろに、あるいはその両方として出現する可能性があるカッコのペアによって異なります。配列型は次によって示されます。

カッコのペアが型と宣言子の両方に出現する配列変数宣言内、またはカッコのペアが仮パラメータ・リストの前と後ろの両方に出現するメソッド宣言内には「複合表記」を使用しないことをお薦めします。

例10.2-2.配列変数および配列型

次のローカル変数宣言文

byte[] rowvector, colvector, matrix[];

は次と同等です。

byte rowvector[], colvector[], matrix[][];

これは、各ローカル変数の配列型が変更されないためです。同様に、次のローカル変数宣言文

int a, b[], c[][];

は次の一連の宣言文と同等です。

int a;
int[] b;
int[][] c;

カッコはCおよびC++の慣例に同意し、宣言内でカッコを使用することが許可されています。ただし、変数宣言の一般的なルールでは、型と宣言の両方にカッコを使用することが許可されているため、次のローカル変数宣言文

float[][] f[][], g[][][], h[];  // Yechh!

は次の一連の宣言と同等です。

float[][][][] f;
float[][][][][] g;
float[][][] h;

配列型の形成方法のため、次のパラメータ宣言は同じ配列型を持ちます。

void m(int @A [] @B []  x) {}
void n(int @A [] @B ... y) {}

また、想定外ですが、次のフィールド宣言は同じ配列型を持ちます。

int @A [] f @B [];
int @B [] @A [] g;

配列オブジェクトがいったん作成されると、その長さは一切変更されません。配列変数が異なる長さの配列を参照できるようにするには、異なる配列に対する参照を変数に割り当てる必要があります。

配列の長さはその型の一部ではないため、配列型の単一の変数に、異なる長さの配列への参照を含めることができます。

配列変数vに型A[]があり、Aが参照型である場合、vには、任意の配列型B[]のインスタンスへの参照を格納できますが、これはBAへの割当てが可能であることが前提です(5.2)。この結果、後の割当てで実行時例外が発生する可能性があります。詳細は、10.5を参照してください。

第13章: バイナリ互換性

13.1 バイナリの形式

比較ドキュメントは、レコードをサポートするためにJava仮想マシン仕様に必要な変更点を説明します。

プログラムは、Java仮想マシン仕様、Java SE 14 Editionによって規定されているclassファイル形式に、またはJavaプログラミング言語で作成されたクラス・ローダーによってこの形式にマップできる表現にコンパイルする必要があります。

クラスまたはインタフェース宣言に対応するclassファイルには、特定の特性が必要です。これらの特性の多くは、バイナリの互換性を確保するソース・コードの変換をサポートするように明確に選択されています。必須特性は次のとおりです。

  1. クラスまたはインタフェースはバイナリ名によって名前を付ける必要があり、これは次の制約を満たす必要があります。

    • 最上位クラスまたはインタフェース(7.6)のバイナリ名はその正規名(6.7)です。

    • メンバー・クラスまたはインタフェース(8.59.5)のバイナリ名は、直前と直後のクラスまたはインタフェースのバイナリ名と、その後に続く$とメンバーの単純名で構成されます。

    • ローカル・クラスまたはインタフェース(14.3)のバイナリ名の構成は、それを直接包含するクラスまたはインタフェースのバイナリ名、その後ろに$、空でない数字のシーケンス、ローカル・クラスの単純名が続きます。

    • 匿名クラス(15.9.5)のバイナリ名は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名と、その後に続く$、および空でない数字のシーケンスで構成されます。

    • 汎用クラスまたはインタフェースによって宣言される型変数のバイナリ名(8.1.29.1.2)は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名、その後ろに順に続く$、型変数の単純名で構成されます。

    • 汎用メソッド(8.4.4)で宣言された型変数のバイナリ名は、そのメソッドを宣言するクラスまたはインタフェースのバイナリ名と、その後に続く$、メソッドの記述子(JVMS §4.3.3)、$、および型変数の単純名で構成されます。

    • 汎用コンストラクタ(8.8.4)で宣言された型変数のバイナリ名は、そのコンストラクタを宣言するクラスのバイナリ名と、その後に続く$、コンストラクタの記述子(JVMS §4.3.3)、$、および型変数の単純名で構成されます。

  2. 別のクラスまたはインタフェースへの参照は、クラスまたはインタフェースのバイナリ名を使用するシンボリックである必要があります。

  3. 定数変数([4.12.4])であるフィールドへの参照は、コンパイル時に定数変数のイニシャライザが示す値Vに解決される必要があります。

    このようなフィールドがstaticである場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。これには、このフィールドを宣言したクラスまたはインタフェースも含まれます。このようなフィールドは常に、初期化(12.4.2)されているものとして示される必要があり、このフィールドのデフォルトの初期値は(Vとは異なる場合)、表示されないようにします。

    このようなフィールドが非staticである場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。ただし、このフィールドが含まれるクラス内は除きます。(インタフェースにはstaticフィールドしかないため、これはインタフェースではなくクラスです。)このクラスには、インスタンス作成時(12.5)にフィールドの値をVに設定するためのコードが必要です。

  4. クラスC内でのフィールド・アクセスを示す正当な式が与えられ、定数値ではなく、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているfという名前のフィールドを参照している場合、Oracleではフィールド参照の修飾型を次のように定義します。

    • 式が単純名によって参照されているときに、fが現在のクラスまたはインタフェースCのメンバーである場合、TCにします。そうでない場合、Tは、fがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。どちらの場合も、Tが参照の修飾型です。

    • 参照の形式がTypeName.fであり、TypeNameがクラスまたはインタフェースを示している場合、TypeNameが示すクラスまたはインタフェースが参照の修飾型です。

    • 式の形式がExpressionName.fまたはPrimary.fである場合は次のようになります。

      • ExpressionNameまたはPrimaryのコンパイル時の型が交差型V1 & ... & Vn (4.9)である場合、参照の修飾型はV1です。

      • そうでない場合、ExpressionNameまたはPrimaryのコンパイル時の型が参照の修飾型です。

    • 式の形式がsuper.fである場合、Cのスーパークラスが参照の修飾型です。

    • 式の形式がTypeName.super.fである場合、TypeNameが示すクラスのスーパークラスが参照の修飾型です。

    fへの参照は、参照の修飾型のイレイジャ(4.6)およびフィールドの単純名fへのシンボリック参照にコンパイルする必要があります。また、型が想定どおりであるかどうかを検証者がチェックできるように、この参照には、宣言されたフィールドの型のイレイジャへのシンボリック参照も含まれている必要があります。

  5. クラスまたはインタフェースC内のメソッド呼出し式またはメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されている(または暗黙的な宣言されている(9.2)) mという名前のメソッドを参照している場合、Oracleではメソッド呼出しの修飾型を次のように定義します。

    • DObjectである場合、式の修飾型はObjectです。

    • そうでない場合は、次のようになります。

      • メソッドが単純名によって参照されているときに、mが現在のクラスまたはインタフェースCのメンバーである場合、TCにします。そうでない場合、Tは、mがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。どちらの場合も、Tがメソッド呼出しの修飾型です。

      • 式の形式がTypeName.mまたはReferenceType::mである場合、TypeNameまたはReferenceTypeが示す型がメソッド呼出しの修飾型です。

      • 式の形式がExpressionName.mPrimary.mExpressionName::mまたはPrimary::mである場合、次のようになります。

        • ExpressionNameまたはPrimaryのコンパイル時の型が交差型V1 & ... & Vn (4.9)である場合、メソッド呼出しの修飾型はV1です。

        • そうでない場合、ExpressionNameまたはPrimaryのコンパイル時の型がメソッド呼出しの修飾型です。

      • 式の形式がsuper.mまたはsuper::mである場合、Cのスーパークラスがメソッド呼出しの修飾型です。

      • 式の形式がTypeName.super.mまたはTypeName.super::mであり、TypeNameがクラスXを示している場合、Xがメソッド呼出しの修飾型です。また、TypeNameがインタフェースXを示している場合、Xがメソッド呼出しの修飾型です。

    メソッドへの参照はコンパイル時に、呼出しの修飾型のイレイジャ(4.6)およびメソッドのシグネチャ(8.4.2)のイレイジャへのシンボリック参照に解決する必要があります。メソッドのシグネチャには、15.12.3で確認されたとおりに次のすべてが含まれている必要があります。

    • メソッドの単純名

    • メソッドに対するパラメータ数

    • 各パラメータの型へのシンボリック参照

    また、メソッドへの参照には、示されるメソッドの戻り型のイレイジャへのシンボリック参照が含まれているか、示されるメソッドがvoidを宣言され、値を戻さないことを示すものが含まれている必要があります。

  6. クラス・インスタンス作成式(15.9)、明示的なコンストラクタ呼出し文(8.8.7.1)、またはクラスまたはインタフェースC内のClassType :: new (15.13)形式のメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているコンストラクタmを参照している場合、Oracleでは、コンストラクタ呼出しの修飾型を次のように定義します。

    • 式の形式がnew D(...)ExpressionName.new D(...)Primary.new D(...)またはD :: newである場合、呼出しの修飾型はDです。

    • 式の形式がnew D(...){...}ExpressionName.new D(...){...}またはPrimary.new D(...){...}である場合、式の修飾型は式のコンパイル時の型です。

    • 式の形式がsuper(...)ExpressionName.super(...)またはPrimary.super(...)である場合、式の修飾型はCの直接スーパークラスです。

    • 式の形式がthis(...)である場合、式の修飾型はCです。

    コンストラクタへの参照はコンパイル時に、呼出しの修飾型のイレイジャ(4.6)およびコンストラクタのシグネチャ(8.8.2)へのシンボリック参照に解決する必要があります。コンストラクタのシグネチャには、次の両方が含まれている必要があります。

    • コンストラクタのパラメータ数

    • 各仮パラメータの型へのシンボリック参照

クラスまたはインタフェースのバイナリ表現には、次もすべて含まれる必要があります。

  1. これがクラスであり、Objectではない場合、このクラスの直接スーパークラスのイレイジャへのシンボリック参照。

  2. それぞれの直接スーパーインタフェースのイレイジャへのシンボリック参照(存在する場合)。

  3. フィールドの単純名およびフィールドの型のイレイジャへのシンボリック参照として与えられた、クラスまたはインタフェース内で宣言された各フィールドの仕様。

  4. これがクラスである場合、各コンストラクタの消去されたシグネチャ(前述を参照)。

  5. クラスまたはインタフェース内で宣言された各メソッド(インタフェースについては、暗黙的に宣言されたメソッドを除く(9.2))ごとに、消去されたシグネチャおよび戻り型(前述を参照)。

  6. クラスまたはインタフェースを実装するために必要なコード。

    • インタフェースについては、フィールド・イニシャライザのコード、およびブロック本体を持つ各メソッドの実装(9.4.3)。

    • クラスについては、フィールド・イニシャライザ、インスタンス・イニシャライザおよび静的イニシャライザのコード、ブロック本体を持つ各メソッドの実装(8.4.7)、および各コンストラクタの実装。

  7. すべてのクラスまたはインタフェースに、その正規名(6.7)をリカバリするために十分な情報が含まれている必要があります。

  8. すべてのメンバー・クラスまたはインタフェースに、ソースレベルのアクセス修飾子をリカバリするために十分な情報が含まれている必要があります。

  9. すべてのネストしたクラスまたはインタフェースに、それを直接包含するクラスまたはインタフェース(8.1.3)へのシンボリック参照が含まれている必要があります。

  10. すべてのクラスまたはインタフェースに、そのすべてのメンバー・クラスおよびインタフェース(8.59.5)へのシンボリック参照、およびその本体内で宣言されたネストした他のすべてのクラスおよびインタフェースへのシンボリック参照が含まれている必要があります。

  11. Javaコンパイラによって発行されたコンストラクトは、発行されたコンストラクトがクラス初期化メソッドでないかぎり、ソース・コードで明示的または暗黙的に宣言されたコンストラクトに対応していない場合、合成としてマークする必要があります(JVMS §2.9)。

  12. Javaコンパイラによって発行されたコンストラクトは、ソース・コードで暗黙的に宣言された仮パラメータに対応している場合、必須としてマークする必要があります(8.8.18.8.98.9.315.9.5.1)。

次の仮パラメータは、ソース・コードで暗黙的に宣言されます。

参考までに、次のコンストラクタは、ソース・コードでは暗黙的に宣言されますが、classファイル内で必須としてマークできるのは仮パラメータのみであるため、必須としてはマークされません(JVMS §4.7.24)。

モジュール宣言に対応するclassファイルには、バイナリ名がmodule-infoであり、スーパークラス、スーパーインタフェース、フィールドおよびメソッドを持たないクラスのclassファイルのプロパティが必要です。また、このモジュールのバイナリ表現には次のものがすべて含まれている必要があります。

次の各セクションでは、既存のバイナリとの互換性を損なわずにクラスおよびインタフェース宣言に対して加えられる可能性のある変更点について説明します。前述の翻訳要件に基づき、Java仮想マシンおよびそのclassファイル形式はこれらの変更をサポートしています。前述の要件に基づくクラス・ローダーによってclassファイルにマップし戻される圧縮または暗号化された表現など、他の有効なバイナリ形式もすべて、これらの変更を必然的にサポートします。

13.4 クラスの展開

13.4.27 レコード・クラスの展開

レコード宣言内のレコード・コンポーネントを追加、削除、変更または順序変更すると、既存のバイナリとの互換性が損なわれ、リンク時間のエラー、すなわちIncompatibleClassChangeErrorが発生する可能性があります。

13.4.8および13.4.12で説明されているとおり、レコード・コンポーネントを削除すると、対応する暗黙のフィールド宣言および対応するアクセサ・メソッド宣言が削除され、標準コンストラクタおよび他のサポート・メソッドのシグネチャと実装も変更されます。

その他すべての点において、レコード・クラスのバイナリ互換性ルールは、標準クラスのルールと同じです。

第14章: ブロックおよび文

14.3 ローカル・クラス宣言

ローカル・クラスまたはローカル・インタフェースは、ネストしたクラスまたはインタフェース(89)であり、その宣言はブロックに直接含まれます(14.2)。

LocalClassOrInterfaceDeclaration:
ClassDeclaration
EnumDeclaration
RecordDeclaration
InterfaceDeclaration

注釈インタフェース(9.6)はローカル・インタフェースとして宣言されない場合があります。

ローカル・クラスおよびインタフェース宣言は、ブロック内の文と自由に混在できます。

ローカル・クラスまたはインタフェースは、パッケージ、クラスまたはインタフェースのメンバーではありません。無名クラス(15.9.5)とは異なり、ローカル・クラスまたはインタフェースには単純名があります(6.2, 6.7)。

ローカルenumクラス、レコード・クラスおよびローカル・インタフェースは暗黙的にstaticです(8.1.1.49.1.1.3)。暗黙的にstaticでないローカル・クラスは内部クラスです(8.1.3)。

ローカル・クラスまたはインタフェースがpublicprotectedまたはprivate (6.6)のいずれかのアクセス修飾子、または修飾子static (8.1.1)で宣言されている場合は、コンパイル時にエラーが発生します。

ローカル・クラスまたはインタフェース宣言のスコープおよびシャドウ化については、6.3および6.4に規定されています。

例14.3-1.ローカル・クラスおよびインタフェースの宣言

ここでは、前述のルールのいくつかの項目の例を示します。

class Global {
    class Cyclic {}

    void foo() {
        new Cyclic(); // create a Global.Cyclic
        class Cyclic extends Cyclic {} // circular definition

        {
            class Local {}
            {
                class Local {} // compile-time error
            }
            class Local {} // compile-time error
            class AnotherLocal {
                void bar() {
                    class Local {} // ok
                }
            }
        }
        class Local {} // ok, not in scope of prior Local
    }
}

メソッドfooの最初の文は、ローカル・クラス宣言のスコープの前に出現しているため、ローカル・クラスCyclicのインスタンスではなく、メンバー・クラスGlobal.Cyclicのインスタンスを作成しています。

ローカル・クラス宣言のスコープが(本体のみではなく)宣言全体を含んでいることは、ローカル・クラスCyclicの定義がGlobal.Cyclicではなくそれ自体を拡張しているため、真に周期的であることを意味します。その結果、ローカル・クラスCyclicの宣言はコンパイル時に拒否されます。

ローカル・クラス名は同じメソッド(または場合によってはコンストラクタまたはイニシャライザ)内で再宣言することはできないため、Localの2回目と3回目の宣言の結果、コンパイル時にエラーが発生します。ただし、Localは、AnotherLocalなど、より深くネストした別のクラス内のコンテキスト内で再宣言できます。

Localの最後の宣言は、Localの前の宣言のスコープ外で行われているため正当です。

第16章: 明確な割当て

各ローカル変数(14.4)およびすべての空のfinalフィールド([4.12.4]、8.3.1.2)には、その値へのアクセスが出現したとき、値が明確に割り当てられている必要があります。

その値へのアクセスは、式内で単純割当て演算子= ([15.26.1])の左側のオペランド以外の場所に出現する変数の単純名(つまり、フィールドの場合はthisで修飾されたフィールドの単純名)で構成されます。

ローカル変数または空のfinalフィールドxのすべてのアクセスについて、アクセスの前にxを明確に割り当てる必要があります。そうしないと、コンパイル時にエラーが発生します。

同様に、すべての空のfinal変数は最大で1回割り当てる必要があります。割当てが出現したときには、明確に割当てが解除されている必要があります。

このような割当ては、変数の単純名(つまり、フィールドの場合はthisで修飾されたその単純名)が割当て演算子の左側に出現する場合にのみ出現出現するように定義されます。

これは、割当ての出現をイタリック表記にするための編集上の変更です。

...