このドキュメントでは、一貫性のあるクラスおよびインタフェースの用語とローカル静的インタフェースおよびenumクラスによる変更に伴う、Java SE 15のプレビュー機能であるレコードをサポートするためのJava言語仕様の変更点について説明します。この機能の概要については、「JEP 384」を参照してください。
変更点は、次のことを除き、Java SE 14のレコードの最初のプレビューと同じです。
すべてのテキストで、一貫性のあるクラスおよびインタフェースの用語の用語が使用されるようになりました
ローカル・レコードの処理は、ローカル静的インタフェースおよびenumクラスの変更に基づいて構築されるようになりました
4.12.4への不要な変更が削除されました
8.10.1 レコード・コンポーネントの
final
修飾子の可能性が削除されました8.10.1 レコード・コンポーネントの注釈は、その注釈型がレコード・コンポーネントのコンテキストに適用できる場合にのみコンポーネントに残ることを明確にしました
8.10.1
@SafeVarArgs
注釈の使用に関するテキストが修正されました8.10.4 標準コンストラクタが
public
である必要があるという要件が削除されました。アクセス修飾子は、少なくともレコード・クラスと同程度のアクセスを提供する必要があります。標準コンストラクタが暗黙的に宣言されている場合、そのアクセス修飾子はレコード・クラスと同じです。8.10.4 コンストラクタの仮パラメータ・リストの各仮パラメータは、対応するレコード・コンポーネントと同じ名前および型である必要があるという要件が追加されました。対応するレコード・コンポーネントが可変個引数レコード・コンポーネントである場合にのみ、仮パラメータは可変個引数パラメータである可能性があります。
8.10.4 コンパクト・コンストラクタの本体のインスタンス・フィールドへの代入はエラーであるという新しい要件が追加されました。
9.6.4.4
@Override
注釈を使用してメソッドがレコード・コンポーネントのアクセサ・メソッドであることを宣言する新しいケース。
比較ドキュメントは、レコードをサポートするためにJava仮想マシン仕様に必要な変更点を説明します。
これ以外のコンパニオン・ドキュメントでは、直列化可能レコードをサポートするためのJavaオブジェクト直列化仕様の変更点について説明します。
変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。
第1章: 概要
1.1 仕様の編成
...
第8章では、クラスについて説明します。クラスのメンバーは、クラス、インタフェース、フィールド(変数)およびメソッドです。クラス変数はクラスごとに1つ存在します。クラス・メソッドは、特定のオブジェクトを参照せずに動作します。インスタンス変数は、クラスのインスタンスであるオブジェクト内に動的に作成されます。インスタンス・メソッドは、クラスのインスタンス上で呼び出されます。このようなインスタンスは、実行時に現在のオブジェクトthis
になり、オブジェクト指向のプログラミング・スタイルをサポートします。
クラスは単一継承をサポートしており、この場合、各クラスが単一のスーパークラスを持ちます。各クラスは、スーパークラスからメンバーを継承し、最終的にはクラスObject
から継承します。クラス型の変数は、そのクラスまたはそのクラスのサブクラスのインスタンスを参照できるため、多相的に既存のメソッドとともに新しい型を使用できます。
クラスは、synchronized
メソッドを使用した同時プログラミングをサポートします。メソッドは、実行によって発生する可能性があるチェック済例外を宣言します。これにより、コンパイル時のチェックが可能になり、例外条件を確実に処理できるようになります。オブジェクトは、オブジェクトがガベージ・コレクタによって破棄される前に呼び出されるfinalize
メソッドを宣言します。これにより、オブジェクトが状態をクリーン・アップできるようになります。
簡潔にするために、この言語には、クラスの実装とは別個の宣言「ヘッダー」や、別個の型およびクラス階層はありません。
特別な形式のクラスであるenumクラスは、小規模な値セットの定義およびその操作を型安全な方法でサポートします。enumクラスは、小規模な値セットの定義をサポートする特別な種類のクラスで、型安全な方法で使用できます。他の言語の列挙とは異なり、enum定数はオブジェクトであり、独自のメソッドを持つ場合があります。
レコード・クラスは、値の集計として機能する単純なオブジェクトの簡略式をサポートするもう1つの特別な種類のクラスです。
...
1.5 プレビュー機能
プレビュー機能の説明には、「レコード型」というテキストが追加されます。
レコード型に関連付けられた重要なAPI要素は、次のとおりです。
- クラス
java.lang.Record
。 java.lang.annotation.ElementType
内のenum定数RECORD_COMPONENT
。
第3章: 字句構造
3.8 識別子
識別子は、Javaの文字およびJavaの数字からなる無限長のシーケンスで、先頭はJavaの文字です。
- Identifier:
- Keyword、BooleanLiteral、NullLiteralのいずれでもない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」を参照してください。
識別子の例は、次のとおりです。
String
i3
- αρετη
MAX_VALUE
isLetterOrDigit
識別子var
、およびyield
およびrecord
は、一部のコンテキストでは許可されないため制限付き識別子です。
型識別子は、文字列文字列var
または文字列yield
でない識別子var
、yield
およびrecord
以外の識別子です。
- TypeIdentifier:
var
、またはyield
またはrecord
以外の識別子
型識別子は、型の宣言または使用が含まれる特定のコンテキストで使用されます。たとえば、クラスの名前はTypeIdentifierである必要があるため、
var
、またはyield
またはrecord
という名前のクラスを宣言することは不正です(8.1)。
非修飾メソッド識別子は、文字列yield
でない識別子です。
- UnqualifiedMethodIdentifier:
yield
以外の識別子
この制限によって、
yield
をyield
文(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コンパイラがより適切なエラー・メッセージを作成できるようになります。キーワード_
(アンダースコア)は、将来パラメータ宣言で使用できるように予約されています。
場合によっては、様々な文字列が誤ってキーワードであるとみなされます。
true
およびfalse
はキーワードではなく、booleanリテラルです(3.10.3)。
null
はキーワードではなく、nullリテラルです(3.10.7)。
var
、およびyield
およびrecord
はキーワードではなく、制限付き識別子です(3.8)。var
は、ローカル変数宣言の型(14.4、14.14.1、14.14.2、14.20.3)およびラムダ仮パラメータの型(15.27.1)として特別な意味を持ちます。yield
は、yield
文で特別な意味を持ちます(14.21)。yield
という名前のメソッドの呼出しはすべて、yield
文と区別するために修飾する必要があります。record
は、レコード宣言内で特別な意味を持ちます(8.10)。
これ以外の文字列として、open
、module
、requires
、transitive
、exports
、opens
、to
、uses
、provides
およびwith
の10種類の制限付きキーワードがあります。これらの文字列は、ModuleDeclaration、ModuleDirectiveおよびRequiresModifierプロダクションの端末として表示される場合のみ、キーワードとしてトークン化されます(7.7)。これらは、それ以外のすべての場所で識別子としてトークン化されます。これにより、制限付きキーワードの導入前に作成されたプログラムとの互換性を確保します。例外が1つあります。文字列transitive
は、ModuleDirectiveプロダクション内の文字列requires
のすぐ右側で、後ろにセパレータが続かないかぎり、キーワードとしてトークン化されます。この場合、これは識別子としてトークン化されます。
第4章: 型、値および変数
4.11 型の使用場所
型は、ほとんどの種類の宣言内および特定の種類の式内で使用されます。特に、型を使用する1617の型コンテキストがあります。
宣言内:
式内:
明示的なコンストラクタ呼出し文、クラス・インスタンス作成式またはメソッド呼出し式の明示的な型引数リスト内の型(8.8.7.1、15.9、15.12)
インスタンス化対象のクラス型として(15.9)またはインスタンス化対象の無名クラスの直接スーパークラスまたは直接スーパーインタフェースとしての非修飾クラス・インスタンス作成式内(15.9.5)
配列作成式内の要素型(15.10.1)
キャスト式のキャスト演算子内の型(15.16)
instanceof
関係演算子に続く型(15.20.2)メンバー・メソッドを検索するための参照型として、またはコンストラクトに対するクラス型または配列型としてのメソッド参照式内(15.13)。
また、型は次としても使用されます。
前述の任意のコンテキスト内の配列型の要素型
前述の任意のコンテキスト内でパラメータ化された型の非ワイルドカード型引数またはワイルドカード型引数の境界。
最後に、Javaプログラミング言語には、型の使用方法を示す3つの特別な語句があります。
型コンテキストの型の意味は、次によって与えられます。
4.2: プリミティブ型の場合
4.4: 型パラメータの場合
4.5: パラメータ化されたクラスおよびインタフェース型、またはパラメータ化された型内の型引数として、またはパラメータ化された型内のワイルドカード型引数の境界として表示されるクラスおよびインタフェース型の場合
[4.8]: RAWであるクラスおよびインタフェース型の場合
4.9: 型パラメータの境界内の交差型の場合
10.1: 配列型の場合
一部の型コンテキストは、参照型をパラメータ化する方法を制限します。
次の型コンテキストでは、型がパラメータ化された参照型である場合、ワイルドカード型引数がないことが必須となります。
インタフェース宣言の
extends
句内(9.1.3)インスタンス化対象のクラス型として(15.9)またはインスタンス化対象の無名クラスの直接スーパークラスまたは直接スーパーインタフェースとしての非修飾クラス・インスタンス作成式内(15.9.5)
メンバー・メソッドを検索するための参照型として、またはコンストラクトに対するクラス型または配列型としてのメソッド参照式内(15.13)。
また、明示的なコンストラクタ呼出し文、クラス・インスタンス作成式、メソッド呼出し式、またはメソッド参照式に対する明示的な型引数リストでは、ワイルドカード型引数は許可されません(8.8.7.1、15.9、15.12、15.13)。
次の型コンテキストでは、型がパラメータ化された参照型である場合、制限のないワイルドカード型引数のみがある(つまり、具象化可能型である)ことが必須となります。
次の型コンテキストでは、パラメータ化された参照型には例外が含まれ、例外の型が非汎用であるため、これを一緒に使用することは許可されません(6.1)。
型が使用される任意の型コンテキストで、プリミティブ型を表すキーワード、または参照型の単純名を表すIdentifierに注釈を付けることができます。また、配列型内のネストの目的のレベルで
[
の左側に注釈を付けることにより、配列型に注釈を付けることもできます。これらの場所にある注釈は型注釈と呼ばれ、9.7.4に規定されています。いくつか例を挙げます。
@Foo int[] f;
は、プリミティブ型int
に注釈を付けます
int @Foo [] f;
は、配列型int[]
に注釈を付けます
int @Foo [][] f;
は、配列型int[][]
に注釈を付けます
int[] @Foo [] f;
は、配列型int[][]
のコンポーネント型である配列型int[]
に注釈を付けます
宣言内に現れる
5つ6つの型コンテキストは、宣言コンテキストと同じ数の構文上の場所を占有します(9.6.4.1)。
メソッドの戻り型(注釈型の要素の型を含む)
クラスまたはインタフェースのフィールド宣言内の型(enum定数を含む)
レコード・クラスのレコード・コンポーネント宣言の型
メソッド、コンストラクタまたはラムダ式の仮パラメータ宣言内の型
ローカル変数制限内の型
例外パラメータ宣言内の型
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); }
}
この例では、型は次の宣言に使用されています。
インポートされた型(7.5): ここでは、パッケージ
java.util
の型java.util.Random
からインポートされた型Random
が宣言されていますクラスのクラス変数およびインスタンス変数(8.3)、およびインタフェースの定数(9.3)であるフィールド: ここでは、クラス
MiscMath
のフィールドdivisor
が型int
であると宣言されていますメソッド・パラメータ(8.4.1): ここでは、メソッド
ratio
のパラメータl
が型long
であると宣言されていますメソッド結果(8.4): ここでは、メソッド
ratio
の結果が型float
であると宣言され、メソッドgausser
の結果が型double
であると宣言されていますコンストラクタ・パラメータ(8.8.1): ここでは、
MiscMath
のコンストラクタのパラメータが型int
であると宣言されていますローカル変数(14.4、14.14): メソッド
gausser
のローカル変数r
およびval
が型Random
およびdouble[]
(double
の配列)であると宣言されています例外パラメータ(14.20): ここでは、
catch
句の例外パラメータe
が型Exception
であると宣言されています型パラメータ(4.4): ここでは、
MiscMath
の型パラメータが、宣言された境界として型Number
を持つ型変数T
であると宣言されていますパラメータ化された型を使用する宣言内: ここでは、パラメータ化された型
Collection<Number>
内の型引数(4.5.1)として型Number
が使用されています。
および次の種類の式内:
クラス・インスタンス作成(15.9): ここでは、型
Random
を使用するクラス・インスタンス作成式により、メソッドgausser
のローカル変数r
が初期化されています汎用クラス(8.1.2)インスタンス作成(15.9): ここでは、式
new ArrayList<Number>()
内の型引数としてNumber
が使用されています配列作成(15.10.1): ここでは、
double
の配列をサイズ2で作成する配列作成式により、メソッドgausser
のローカル変数val
が初期化されています汎用メソッド(8.4.4)またはコンストラクタ(8.8.4)の呼出し(15.12): ここでは、メソッド
loop
が明示的な型引数S
を使用してそれ自体を呼び出していますキャスト(15.16): ここでは、メソッド
ratio
のreturn
文がキャスト内でfloat
型を使用していますinstanceof
演算子(15.20.2): ここでは、instanceof
演算子により、e
が型ArithmeticException
と割当て互換しているかどうかがテストされています
第6章: 名前
6.1 宣言
宣言により、エンティティがプログラムに導入され、このエンティティを参照するために名前内で使用できる識別子(3.8)が組み込まれます。この識別子には、導入されるエンティティがクラス、インタフェースまたは型パラメータである場合は型識別子であるという制約があります。
宣言されたエンティティは、次のいずれかです。
module
宣言で宣言されたモジュール(7.7)package
宣言で宣言されたパッケージ(7.4)- single-type-import宣言で宣言された、インポートされたクラスまたはインタフェース
またはtype-import-on-demand宣言で宣言された、インポートされたクラスまたはインタフェース(7.5.1、7.5.2)
単一静的インポート宣言またはオンデマンド静的インポート宣言で宣言された、インポートされた
static
メンバー(7.5.3、7.5.4)汎用クラス、インタフェース、メソッドまたはコンストラクタの宣言の一部として宣言された型パラメータ(8.1.2、9.1.2、8.4.4、8.8.4)
enum定数(8.9.1)
レコード・コンポーネント(8.10.3)
クラスまたはインタフェースのメソッド(8.4.1)、クラスのコンストラクタ(8.8.1)またはラムダ式(15.27.1)の仮パラメータ
try
文のcatch
句で宣言された例外ハンドラの例外パラメータ(14.20)ローカル変数で、次のいずれか:
次のいずれかで宣言されたローカル・クラスまたはインタフェース(14.3):
クラス宣言
enum宣言
レコード宣言
インタフェース宣言
コンストラクタ(8.8)も宣言によって導入されますが、新しい名前が導入されるのではなく、そのコンストラクタが宣言されたクラスの名前が使用されます。
...
6.5 名前の意味の確認
6.5.1 コンテキストに応じた名前の構文的分類
次のコンテキストでは、名前が構文的にModuleNameとして分類されます。
次のコンテキストでは、名前が構文的にPackageNameとして分類されます。
モジュール宣言内の
exports
またはopens
の右側修飾されたPackageName内の「
.
」の左側
次のコンテキストでは、名前が構文的にTypeNameとして分類されます。
最初の11の非汎用コンテキスト(6.1):
モジュール宣言内の
uses
またはprovides
ディレクティブ内(7.7.1)単一型インポート宣言内(7.5.1)
単一静的インポート宣言内の
.
の左側(7.5.3)オンデマンド静的インポート宣言内の
.
の左側(7.5.4)コンストラクタ宣言内の
(
の左側(8.8)注釈内の
@
記号の後(9.7)クラス・リテラル内の
.class
の左側(15.8.2)修飾された
this
式の.this
の左側(15.8.4)修飾されたスーパークラス・フィールド・アクセス式の
.super
の左側(15.11.2)修飾されたメソッド呼出し式内の
.
Identifierまたは.super.
Identifierの左側(15.12)メソッド参照式内の
.super::
の左側(15.13)
型が使用されている
1617のコンテキスト内のReferenceType (配列型内のカッコの左側にあるReferenceType、パラメータ化された型内の<の左側、パラメータ化された型の非ワイルドカード型引数内、またはパラメータ化された型のワイルドカード型引数のextends
またはsuper
句内を含む)を構成する識別子または点線付きの識別子シーケンスとして(4.11):インタフェース宣言の
extends
句内(9.1.3)汎用クラス、インタフェース、メソッドまたはコンストラクタの型パラメータ宣言の
extends
句内(8.1.2、9.1.2、8.4.4、8.8.4)メソッドのレシーバ・パラメータの型(8.4)
例外パラメータ宣言内の型(14.20)
レコード・コンポーネントの型(8.10.1)
11.12.明示的なコンストラクタ呼出し文、クラス・インスタンス作成式、またはメソッド呼出し式に対する明示的な型引数リスト内(8.8.7.1、15.9、15.12)12.13.非修飾クラス・インスタンス作成式内で、インスタンス化するクラス型として(15.9)、またはインスタンス化する無名クラスの直接スーパークラスまたは直接スーパーインタフェースとして(15.9.5)13.14.配列作成式内の要素型(15.10.1)14.15.キャスト式のキャスト演算子内の型(15.16)15.16.instanceof
関係演算子に続く型(15.20.2)16.17.メンバー・メソッドを検索するための参照型として、またはコンストラクトに対するクラス型または配列型としてのメソッド参照式内(15.13)。
前述の16のコンテキスト内のReferenceTypeの識別子からのTypeNameの抽出は、要素型や型引数などのReferenceTypeのサブ用語すべてに繰り返し適用することを意図しています。
たとえば、フィールド宣言に型
p.q.Foo[]
が使用されるとします。配列型のカッコは無視され、用語p.q.Foo
が識別子の点線シーケンスとして配列型内のカッコの左側に抽出され、TypeNameとして分類されます。後のステップで、p
、q
およびFoo
のどれが型名またはパッケージ名であるかが確認されます。別の例として、キャスト演算子に型
p.q.Foo<? extends String>
が使用されるとします。用語p.q.Foo
が識別子用語の点線シーケンスとして再度、今回はパラメータ化された型内の<
の左側に抽出され、TypeNameとして分類されます。用語String
が、パラメータ化された型のワイルドカード型引数のextends
句内の識別子として抽出され、TypeNameとして分類されます。
次のコンテキストでは名前がExpressionNameとして構文的に分類されています。
修飾されたスーパークラス・コンストラクタ呼び出し内で修飾する式として(8.8.7.1)
修飾されたクラス・インスタンス作成式内で修飾する式として(15.9)
配列アクセス式内で配列参照式として(15.10.3)
PostfixExpressionとして(15.14)
割当て演算子の左側のオペランドとして(15.26)
try
-with-resources文内のVariableAccessとして(14.20.3)
次のコンテキストでは、名前が構文的にMethodNameとして分類されます。
- メソッド呼出し式内の「
(
」の前(15.12)
次のコンテキストでは、名前が構文的にPackageOrTypeNameとして分類されます。
修飾されたTypeName内の「
.
」の左側オンデマンド型インポート宣言内(7.5.2)
次のコンテキストでは、名前が構文的にAmbiguousNameとして分類されます。
修飾されたExpressionName内の「
.
」の左側メソッド呼出し式内の「
(
」の前にある右端の.
の左側修飾されたAmbiguousName内の「
.
」の左側注釈型要素宣言のデフォルト値句内(9.6.2)
要素と値のペア内の「
=
」の右側(9.7.1)メソッド参照式内の
::
の左側(15.13)
構文的分類の結果、特定の種類のエンティティを式の特定の部分に制限できるようになります。
フィールド、パラメータまたはローカル変数の名前を式として使用できます(15.14.1)。
メソッドの名前は、式内にメソッド呼出し式の一部としてのみ表示される場合があります(15.12)。
クラスまたはインタフェース型の名前は式内に、クラス・リテラルの一部(15.8.2)、修飾された
this
式(15.8.4)、クラス・インスタンス作成式(15.9)、配列作成式(15.10.1)、キャスト式(15.16)、instanceof
式(15.20.2)、enum定数(8.9)、あるいはフィールドまたはメソッドの修飾名の一部としてのみ出現する場合があります。パッケージの名前は、式内にクラスまたはインタフェース型の修飾名の一部としてのみ表示される場合があります。
第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
として宣言して、同じモジュールの他のパッケージのコードから(および場合によっては他のモジュールのパッケージのコードから)クラスまたはインタフェースにアクセスすることもできます。
最上位クラスまたはインタフェースの宣言にprotected
、private
または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
など)で構成される名前のファイルに見つからない場合はコンパイル時にエラーが発生するという制限を強制することを選択できます。
クラスまたはインタフェースが、クラスまたはインタフェースが宣言されているパッケージの他の通常コンパイル・ユニットのコードによって参照されています。
クラスまたはインタフェースが
public
として宣言されています(したがって、他のパッケージのコードからアクセスできる可能性があります)。
この制限は、1つのコンパイル・ユニットについてそのようなクラスまたはインタフェースを1つまでにする必要があることを意味します。この制限によって、Javaコンパイラはパッケージ内で名前付きクラスまたはインタフェースを容易に見つけることができます。実際には、多くのプログラマは、各クラスまたはインタフェースが
public
であるかどうかや、他のコンパイル・ユニットのコードによって参照されているかどうかに関係なく、クラスまたはインタフェースをその独自のコンパイル・ユニットに配置します。
たとえば、
public
型wet.sprocket.Toad
のソース・コードはディレクトリwet/sprocket
のファイルToad.java
内にあり、対応するオブジェクト・コードは同じディレクトリのファイルToad.class
内にあります。
第8章: クラス
クラス宣言では、新しい参照型を定義し、これらの実装方法について記述します(8.1)。
最上位クラス(7.6)は、コンパイル・ユニットの最上位で宣言されるクラスです。
ネストしたクラスは、宣言が別のクラスまたはインタフェースの本体内で行われるクラスです。ネストしたクラスは、メンバー・クラス(8.5、9.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) public
、protected
または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のサブクラスであるのは、次のいずれかの条件が満たされる場合です。
AがCの直接サブクラスである
クラスBが存在し、AがBのサブクラスであり、Bが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
この場合、これらの関係は次のとおりです。
クラス
Point
は、Object
の直接サブクラスである。クラス
Object
は、クラスPoint
の直接スーパークラスである。クラス
ColoredPoint
は、クラスPoint
の直接サブクラスである。クラス
Point
は、クラスColoredPoint
の直接スーパークラスである。
クラス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; }
この場合、これらの関係は次のとおりです。
クラス
Point
は、クラスColoredPoint
のスーパークラスである。クラス
Point
は、クラスColored3dPoint
のスーパークラスである。クラス
ColoredPoint
は、クラスPoint
のサブクラスである。クラス
ColoredPoint
は、クラスColored3dPoint
のスーパークラスである。クラス
Colored3dPoint
は、クラスColoredPoint
のサブクラスである。クラス
Colored3dPoint
は、クラスPoint
のサブクラスである。
クラスCが型Tに直接依存するのは、TがCのextends
またはimplements
句内で、スーパークラスまたはスーパーインタフェースとして、またはスーパークラスまたはスーパーインタフェース名の完全修飾形式の修飾子として指定されている場合です。
クラスCが参照型Tに依存するのは、次のいずれかの条件が満たされる場合です。
CがTに直接依存する。
Cが、Tに依存(9.1.3)するインタフェースIに直接依存する。
Cが、Tに依存するクラスDに直接依存する(この定義を再帰的に使用します)。
クラスがそれ自体に依存する場合は、コンパイル時にエラーが発生します。
クラスがロードされるときに、循環的に宣言されたクラスが実行時に検出された場合、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.6、9.1.4)に直接包含されているクラスです。メンバー・クラスはenumクラス(8.9)またはレコード・クラス(8.10)である場合があります。
メンバー・インタフェースは、宣言が別のクラスまたはインタフェース宣言の本体(8.1.6、9.1.4)に直接包含されているインタフェースです。メンバー・インタフェースは注釈インタフェース(9.6)である場合があります。
クラス内のメンバー・クラスまたはインタフェース宣言のアクセス可能性は、そのアクセス修飾子によって指定されるか、アクセス修飾子がない場合は6.6によって指定されます。
クラスが特定の名前のメンバー・クラスまたはインタフェースを宣言する場合、そのクラスまたはインタフェースの宣言は、クラスのスーパークラスおよびスーパーインタフェース内の同じ名前のメンバー・クラスおよびインタフェースのすべてのアクセス可能な宣言を隠すと言うことができます。
この観点では、メンバー・クラスおよびインタフェースを隠すことは、フィールドを隠すことに似ています(8.3)。
クラスは、その直接スーパークラスおよび直接スーパーインタフェースから、スーパークラスおよびスーパーインタフェースの非private
メンバー・クラスおよびインタフェースのうち、クラス内のコードにアクセス可能で、かつクラス内の宣言によって隠されないすべてのものを継承します。
クラスは、そのスーパークラスとスーパーインタフェース、またはそのスーパーインタフェースのみから、同じ名前の複数のメンバー・クラスまたはインタフェースを継承できます。そのような状況自体によってコンパイル時にエラーが発生することはありません。ただし、クラスの本体内でそのようなメンバー・クラスまたはインタフェースを単純名で参照しようとすると、参照があいまいなため、コンパイル時にエラーが発生します。
インタフェースから同じメンバー・クラスまたはインタフェースの宣言が継承されるパスが複数ある場合があります。このような状況下では、メンバー・クラスまたはインタフェースは1回のみ継承されたとみなされ、あいまいさなしに単純名で参照できます。
8.8 コンストラクタ宣言
コンストラクタは、クラスのインスタンスであるオブジェクトの作成時に使用されます(12.5、15.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)のメンバーとして宣言できないことを意味します。
レコード宣言に対して同じキーワードが修飾子として複数回出現する場合、またはレコード宣言にアクセス修飾子public
、protected
および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つのレコード・コンポーネントを宣言する場合は、コンパイル時にエラーが発生します。
レコード宣言で名前clone
、finalize
、getClass
、hashCode
、notify
、notifyAll
、toString
またはwait
を持つレコード・コンポーネントを宣言する場合は、コンパイル時にエラーが発生します。
すべてのレコード・コンポーネントには対応するアクセサ・メソッドがある(8.10.3)ため、この制限により、レコード・クラスがクラス
Object
のメソッドをオーバーライドまたはオーバーロードすることが防止されます。
レコード・コンポーネントに対する注釈修飾子のルールは、9.7.4に規定されています。レコード・クラスのレコード・コンポーネントの注釈は、8.10.2および8.10.3に規定されているとおり、レコード・クラスのメンバーおよびコンストラクタに伝播される場合があります。レコード・コンポーネントの注釈は、その注釈型がレコード・コンポーネントのコンテキストに適用できる場合にのみ、レコード・コンポーネントに残ります。
宣言されるレコード・コンポーネントの型は、それが可変個引数レコード・コンポーネントであるかどうかによって異なります。
レコード・コンポーネントが可変個引数レコード・コンポーネントでない場合、宣言された型はUnannTypeで示されます。
レコード・コンポーネントが可変個引数レコード・コンポーネントである場合、宣言される型は10.2に規定されている配列型です。
宣言された可変個引数レコード・コンポーネントの型に型情報保持可能でない要素型(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.4.4)
アクセサ・メソッドが、宣言された
public
であるアクセサ・メソッドが、宣言された
static
ではないアクセサ・メソッドが
throws
句を持たない標準クラス宣言内のメソッドの他のすべてのルールが満たされている(8.4)
そうでない場合、コンパイル時にエラーが発生します。
レコード・コンポーネントのアクセサ・メソッドが明示的に宣言されない場合は、次の特性を持つアクセサ・メソッドが暗黙的に宣言されます。
名前がレコード・コンポーネントの名前と同じである
戻り型が、宣言されたレコード・コンポーネントの型と同じである
汎用ではない。
空の仮パラメータ・リストがある
public
として宣言されている。これに付けられるのが、対応するレコード・コンポーネント内に出現し、注釈型がメソッド宣言コンテキストまたは型コンテキスト、あるいはこれら両方に適用される注釈である。
暗黙的に宣言されたアクセサ・メソッドの本体が、レコード・コンポーネントに対応するフィールドの値のみを返す。
したがって、レコード・クラスには、レコード・コンポーネント・リストに出現するすべてのレコード・コンポーネントのアクセサ・メソッドが含まれます。
レコード・コンポーネントに出現する注釈は、そのレコード・コンポーネントに対して明示的に宣言されたアクセサ・メソッドには伝播されません。これは、対応するレコード・コンポーネントの適用可能な注釈を使用して注釈が付けられる、暗黙的に宣言されたアクセサ・メソッドとは対照的です。
アクセサ・メソッド(明示的または暗黙的)は、レコード・クラスのスーパーインタフェースで宣言されたメソッドをオーバーライドまたはオーバーロードする場合があります。
コンポーネント名(8.10.1)に対する制限は、アクセサ・メソッドにクラス
Object
の非private
メソッドとオーバーライド等価であるシグネチャがないことを意味します。
すべてのレコード・クラスは、クラスRecord
で宣言されたabstract
メソッドの実装を提供します。次の各メソッドでは、レコード・クラスが同じシグネチャ(8.4.2)を持つメソッドを明示的に宣言しない場合、メソッドは暗黙的に宣言されます。
メソッド
public final boolean equals(Object obj)
。これは、型が参照型であるレコード・コンポーネントの場合はクラスObject
のequals
メソッドに従い、型がプリミティブ型であるレコード・コンポーネントの場合は==
に従って、引数が同じレコード・クラスのインスタンスで、かつ、このレコードの各レコード・コンポーネントが、引数の対応するレコード・コンポーネントと等しい場合にのみtrue
を返します。メソッド
public final int hashCode()
。これは、型が参照型であるレコード・コンポーネントの場合はクラスObject
のhashCode
メソッドに従い、型がプリミティブ型であるレコード・コンポーネントの場合はラッパー・クラス(5.1.7)のhashCode
メソッドに従って、すべてのレコード・コンポーネントのハッシュ・コード値を組み合せて導出されたハッシュ・コード値を返します。メソッド
public final String toString()
。これは、型が参照型であるコンポーネントの場合はクラスObject
のtoString
メソッドに従い、型がプリミティブ型であるレコード・コンポーネントの場合はラッパー・クラスのtoString
メソッドに従って、レコード・クラスの名前と、レコード・コンポーネントの名前および文字列表現から導出された文字列を返します。
8.2で説明されているとおり、レコード・クラスは他のメンバーを宣言または継承する場合があります。レコード・クラスのすべてのメンバー(暗黙的に宣言されたメンバーを含む)は、クラス内のメンバー宣言に関する通常のルール(8.3、8.4、8.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つのみ存在できます。
レコード・クラスの標準コンストラクタのアクセス修飾子は、次のように、少なくともレコード・クラスと同程度のアクセスを提供する必要があります。
レコード・クラスが
public
の場合、標準コンストラクタはpublic
である必要があります。そうでない場合、コンパイル時にエラーが発生します。レコード・クラスが
protected
の場合、標準コンストラクタはprotected
またはpublic
である必要があります。そうでない場合、コンパイル時にエラーが発生します。レコード・クラスにパッケージ・アクセス権がある場合、標準コンストラクタは
private
にできません。そうでない場合、コンパイル時にエラーが発生します。レコード・クラスが
private
の場合、標準コンストラクタはアクセス可能性で宣言できます。
コンパクト・コンストラクタではなく、標準コンストラクタであるコンストラクタ宣言は、次の条件を満たす必要があります。
コンストラクタの仮パラメータ・リストの各仮パラメータは、対応するレコード・コンポーネントと同じ名前および型である必要があります。対応するレコード・コンポーネントが可変個引数レコード・コンポーネントである場合のみ、仮パラメータを可変個引数パラメータにする必要があります。
コンストラクタは汎用にできません(8.8.4)。
コンストラクタに
throws
句を含めることはできません。コンストラクタの本体に、明示的なコンストラクタ呼出し文を含めることはできません(8.8.7.1)。
標準クラス宣言内のコンストラクタ宣言のすべてのルールを満たす必要があります(8.8)。
そうでない場合、コンパイル時にエラーが発生します。
これらのルールにより、レコード・コンポーネントの注釈が、明示的に宣言されたコンストラクタの対応する仮パラメータの注釈とは異なる場合があります。たとえば、次が有効です。
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)内のコンストラクタのシグネチャに関するルールに基づき、レコード宣言にコンパクト・コンストラクタ宣言、および標準コンストラクタである標準コンストラクタ宣言が含まれている場合も、コンパイル時にエラーが発生します。コンパクト・コンストラクタは標準コンストラクタです。
コンパクト・コンストラクタ宣言は、次の条件をすべて満たす必要があります。そうでない場合、コンパイル時にエラーが発生します。
コンパクト・コンストラクタの本体に、
return
文が含まれない(14.17)。コンパクト・コンストラクタの本体に、明示的なコンストラクタ呼出し文が含まれない(8.8.7.1)。
標準クラス宣言内のコンストラクタの他のすべてのルールが満たされている(8.8)。ただし、レコード・クラスのレコード・コンポーネントに対応するフィールドを明確に割り当てる必要があるという要件、さらにコンパクト・コンストラクタの末尾で明確に割当て解除できないという要件は除きます(8.3.1.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と同じアクセス修飾子があります。この場合、標準コンストラクタはパッケージ・アクセス権(6.6)を持ちます。
暗黙的に宣言された標準コンストラクタが
throws
句を持たない。暗黙的に宣言された標準コンストラクタの本体が、レコード・コンポーネントがレコード・コンポーネント・リスト内に表示される順序で、対応する仮パラメータを使用してレコード・コンポーネントに対応する各フィールドを初期化する。
レコード・クラスRの宣言に、標準コンストラクタでないコンストラクタの他の宣言がある場合、標準クラス宣言(8.8)内のコンストラクタ宣言のルールを満たすことに加えて、そのような各コンストラクタ(存在する場合)は次の条件を満たす必要があります。
- コンストラクタ本体は、代替のコンストラクタ呼出しで開始する必要があります(8.8.7.1)。
そうでない場合、コンパイル時にエラーが発生します。
第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定数に対応しています。
モジュール宣言(7.7)
java.lang.annotation.ElementType.MODULE
に対応していますパッケージ宣言(7.4.1)
java.lang.annotation.ElementType.PACKAGE
に対応しています型宣言: クラス、インタフェース、enum、レコード、および注釈宣言(8.1.1、9.1.1、8.5、9.5、8.9、8.10、9.6)
java.lang.annotation.ElementType.TYPE
に対応していますまた、注釈宣言は
java.lang.annotation.ElementType.ANNOTATION_TYPE
に対応していますメソッド宣言(注釈インタフェースの要素を含む) (8.4.3、9.4、9.6.1)
java.lang.annotation.ElementType.METHOD
に対応していますコンストラクタ宣言(8.8.3)
java.lang.annotation.ElementType.CONSTRUCTOR
に対応しています汎用クラス、インタフェース、メソッドおよびコンストラクタの型パラメータ宣言(8.1.2、9.1.2、8.4.4、8.8.4)
java.lang.annotation.ElementType.TYPE_PARAMETER
に対応していますフィールド宣言(enum定数を含む) (8.3.1、9.3、8.9.1)
java.lang.annotation.ElementType.FIELD
に対応しています仮パラメータおよび例外パラメータ宣言(8.4.1、9.4、14.20)
java.lang.annotation.ElementType.PARAMETER
に対応していますローカル変数宣言(
for
文のループ変数およびtry
-with-resources文のリソース変数を含む) (14.4、14.14.1、14.14.2、14.20.3)java.lang.annotation.ElementType.LOCAL_VARIABLE
に対応していますレコード・コンポーネント宣言(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の宣言内に存在しない場合、Tは910のすべての宣言コンテキストおよび1617のすべての型コンテキストに適用できます。
9.6.4.4 @Override
プログラマは、メソッド宣言をオーバーライドするときにそれをオーバーロードすることがあり、微妙な問題につながります。注釈インタフェースOverride
は、そのような問題の早期検出をサポートします。
従来の例は、
equals
メソッドに関するものです。プログラマは、クラスFoo
で次のように記述します。public boolean equals(Foo that) { ... }
実際に記述しようとした内容は次のとおりです。
public boolean equals(Object that) { ... }
これは完全に正当ですが、クラス
Foo
はObject
からequals
実装を継承するため、微妙なバグの原因となる可能性があります。
クラスまたはインタフェースTのメソッド宣言に@Override
の注釈が付けられているが、メソッドがTからTのスーパー・タイプ(8.4.8.1、9.4.1.1)で宣言されたメソッドをオーバーライドしない場合、またはObject
のpublic
メソッド(4.3.2、8.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.hashCode
をFoo
からオーバーライドするため、クラス宣言での@Override
の使用は、最初の条項によって正当です。インタフェース宣言については、インタフェースが
Object
をスーパー・タイプとして持たず、Object
のpublic
メンバーに対応するpublic
abstract
メンバーをインタフェースが持つとします(9.2)。インタフェースでそれらを明示的に宣言する(つまり、Object
のpublic
メソッドとオーバーライド等価であるメンバーを宣言する)ことを選択した場合、インタフェースはそれらをオーバーライドするとみなされ、@Override
の使用は許可されます。一方、
clone
メソッドで@Override
を使用しようとするインタフェースを考えてみましょう。(この例では、finalize
を使用することもできます)interface Quux { @Override Object clone(); }
Object.clone
はpublic
でないため、Quux
で暗黙的に宣言されたclone
というメンバーはありません。したがって、Quux
内のclone
の明示的な宣言は他のメソッドを実装するとみなされず、@Override
の使用は誤りです。(Quux.clone
はpublic
であるという事実は関係ありません。)対照的に、
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)に適用できます。たとえば、
A
、B
および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つの宣言コンテキストのいずれかで考えられます。
メソッド宣言(注釈型の要素を含む)
コンストラクタ宣言
フィールド宣言(enum定数を含む)
仮パラメータ宣言および例外パラメータ宣言
ローカル変数宣言(
for
文のループ変数およびtry
-with-resources文のリソース変数を含む)レコード・コンポーネント宣言
Javaプログラミング言語の文法では、これらの場所にある注釈が宣言(8.3)の修飾子として明確に取り扱われますが、これは純粋に構文上だけのものです。注釈が宣言または宣言されたエンティティの型のどちらに適用されるか、つまり、注釈が宣言注釈または型注釈のどちらであるかは、注釈の型の適用性によって決まります。
注釈の型が、宣言に対応する宣言コンテキストに適用可能であり、型コンテキストには適用可能でない場合、注釈は宣言のみに適用されるとみなされます。
注釈の型が、型コンテキストに適用可能であり、宣言に対応する宣言コンテキストには適用可能でない場合、注釈は、その注釈に最も近い型のみに適用されるとみなされます。
注釈の型が、宣言に対応する宣言コンテキストに適用可能であると同時に型コンテキストに適用可能である場合、注釈は、宣言とその注釈に最も近い型の両方に適用されるとみなされます。
前述の2番目のと3番目のケースでは、注釈に最も近い型は、次のように決定されます。
注釈が
void
メソッド宣言、またはvar
を使用するローカル変数宣言(14.4、14.14.2、14.20.3)の前にある場合、最も近い型は存在しません。注釈の型が、その注釈に最も近い型のみに適用されるとみなされる場合、コンパイル時にエラーが発生します。注釈がコンストラクタ宣言の前にある場合、最も近い型は、新しく構築されたオブジェクトの型です。新しく構築されたオブジェクトの型は、コンストラクタ宣言の直前と直後にある型の完全修飾名です。この完全修飾名内で、注釈は、コンストラクタ宣言が示す単純な型名に適用されます。
その他すべてのケースでは、最も近い型は、宣言されたエンティティのソース・コード内に書かれた型です。その型が配列型である場合、その要素型が注釈に最も近いとみなされます。
たとえば、フィールド宣言
@Foo public static String f;
の場合、@Foo
に最も近い型はString
です。(フィールド宣言の型がjava.lang.String
として作成されていた場合、java.lang.String
が、@Foo
に最も近い型になり、後のルールにより、型注釈をパッケージ名java
に適用することが禁止されます。)汎用メソッド宣言@Foo <T> int[] m() {...}
の場合、宣言されたエンティティ用として作成された型はint[]
であるため、@Foo
は要素型int
に適用されます。var
を使用しないローカル変数宣言と、ラムダ式の仮パラメータ宣言は、両方とも、ソース・コードで宣言注釈と型注釈を使用可能であるが、型注釈のみをclass
ファイルに格納できるという点が似ています。
型Tの注釈が次の構文上の修飾子である場合、コンパイル時にエラーが発生します。
モジュール宣言: ただし、Tはモジュール宣言には適用できません。
パッケージ宣言: ただし、Tはパッケージ宣言には適用できません。
クラス、インタフェース、
またはenum、またはレコード宣言: ただし、Tは型宣言、型コンテキストまたは注釈型宣言に適用できません。メソッド宣言(注釈型の要素を含む): ただし、Tはメソッド宣言または型コンテキストに適用できません。
コンストラクタ宣言: ただし、Tはコンストラクタ宣言または型コンテキストに適用できません。
汎用クラス、インタフェース、メソッドまたはコンストラクタの型パラメータ宣言: ただし、Tは型パラメータ宣言または型コンテキストに適用できません。
フィールド宣言(enum定数を含む): ただし、Tはフィールド宣言または型コンテキストに適用できません。
仮パラメータ宣言または例外パラメータ宣言: ただし、Tは仮パラメータ宣言および例外パラメータ宣言または型コンテキストに適用できません。
レシーバ・パラメータ: ただし、Tは型コンテキストに適用できません。
ローカル変数宣言(
for
文のループ変数またはtry
-with-resources文のリソース変数を含む): ただし、Tはローカル変数宣言または型コンテキストに適用できません。レコード・コンポーネント: ただし、Tはレコード・コンポーネント宣言、フィールド宣言、メソッド宣言、仮パラメータ宣言および例外パラメータ宣言、または型コンテキストに適用できません。
これら
911個の条項のうち56つで"...または型コンテキスト"と記載されているのは、これらが、一見すると宣言または宣言されたエンティティの型に注釈を適用できそうな、構文上の5つ6つの場所であるためです。また、クラス、インタフェース、enum、レコードおよび注釈型宣言、さらに型パラメータ宣言に関するこれら9つ11の条項のうち2つの条項で「~または型コンテキスト」と記載されているのは、型に@Target(ElementType.TYPE_USE)
がメタ注釈として付けられている(ため、型コンテキストに適用可能である)注釈を型宣言に適用する方が便利である可能性があるからです。
次の両方が当てはまる場合、型注釈は許容されます。
注釈が最も近い単純名がPackageNameではなくTypeNameとして分類されている。
注釈が最も近い単純名の後ろに「
.
」および別のTypeNameがある。つまり、注釈が@Foo T.U
として出現し、U
がT
の内部クラスを示している。
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
を修飾する名前です。この場合、@Foo
はE
の隣では正当になりません。ただし、技術的には、@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.3、9.3、14.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.3、8.4.1、9.3、9.4、14.4.1、14.14.2、15.27.1、8.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[]
のインスタンスへの参照を格納できますが、これはBのAへの割当てが可能であることが前提です(5.2)。この結果、後の割当てで実行時例外が発生する可能性があります。詳細は、10.5を参照してください。
第13章: バイナリ互換性
13.1 バイナリの形式
比較ドキュメントは、レコードをサポートするためにJava仮想マシン仕様に必要な変更点を説明します。
プログラムは、Java仮想マシン仕様、Java SE 14 Editionによって規定されているclass
ファイル形式に、またはJavaプログラミング言語で作成されたクラス・ローダーによってこの形式にマップできる表現にコンパイルする必要があります。
クラスまたはインタフェース宣言に対応するclass
ファイルには、特定の特性が必要です。これらの特性の多くは、バイナリの互換性を確保するソース・コードの変換をサポートするように明確に選択されています。必須特性は次のとおりです。
クラスまたはインタフェースはバイナリ名によって名前を付ける必要があり、これは次の制約を満たす必要があります。
メンバー・クラスまたはインタフェース(8.5、9.5)のバイナリ名は、直前と直後のクラスまたはインタフェースのバイナリ名と、その後に続く
$
とメンバーの単純名で構成されます。ローカル・クラスまたはインタフェース(14.3)のバイナリ名の構成は、それを直接包含するクラスまたはインタフェースのバイナリ名、その後ろに
$
、空でない数字のシーケンス、ローカル・クラスの単純名が続きます。匿名クラス(15.9.5)のバイナリ名は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名と、その後に続く
$
、および空でない数字のシーケンスで構成されます。汎用クラスまたはインタフェースによって宣言される型変数のバイナリ名(8.1.2、9.1.2)は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名、その後ろに順に続く
$
、型変数の単純名で構成されます。汎用メソッド(8.4.4)で宣言された型変数のバイナリ名は、そのメソッドを宣言するクラスまたはインタフェースのバイナリ名と、その後に続く
$
、メソッドの記述子(JVMS §4.3.3)、$
、および型変数の単純名で構成されます。汎用コンストラクタ(8.8.4)で宣言された型変数のバイナリ名は、そのコンストラクタを宣言するクラスのバイナリ名と、その後に続く
$
、コンストラクタの記述子(JVMS §4.3.3)、$
、および型変数の単純名で構成されます。
別のクラスまたはインタフェースへの参照は、クラスまたはインタフェースのバイナリ名を使用するシンボリックである必要があります。
定数変数([4.12.4])であるフィールドへの参照は、コンパイル時に定数変数のイニシャライザが示す値Vに解決される必要があります。
このようなフィールドが
static
である場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。これには、このフィールドを宣言したクラスまたはインタフェースも含まれます。このようなフィールドは常に、初期化(12.4.2)されているものとして示される必要があり、このフィールドのデフォルトの初期値は(Vとは異なる場合)、表示されないようにします。このようなフィールドが非
static
である場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。ただし、このフィールドが含まれるクラス内は除きます。(インタフェースにはstatic
フィールドしかないため、これはインタフェースではなくクラスです。)このクラスには、インスタンス作成時(12.5)にフィールドの値をVに設定するためのコードが必要です。クラスC内でのフィールド・アクセスを示す正当な式が与えられ、定数値ではなく、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているfという名前のフィールドを参照している場合、Oracleではフィールド参照の修飾型を次のように定義します。
式が単純名によって参照されているときに、fが現在のクラスまたはインタフェースCのメンバーである場合、TをCにします。そうでない場合、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へのシンボリック参照にコンパイルする必要があります。また、型が想定どおりであるかどうかを検証者がチェックできるように、この参照には、宣言されたフィールドの型のイレイジャへのシンボリック参照も含まれている必要があります。
クラスまたはインタフェースC内のメソッド呼出し式またはメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されている(または暗黙的な宣言されている(9.2)) mという名前のメソッドを参照している場合、Oracleではメソッド呼出しの修飾型を次のように定義します。
Dが
Object
である場合、式の修飾型はObject
です。そうでない場合は、次のようになります。
メソッドが単純名によって参照されているときに、mが現在のクラスまたはインタフェースCのメンバーである場合、TをCにします。そうでない場合、Tは、mがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。どちらの場合も、Tがメソッド呼出しの修飾型です。
式の形式がTypeName
.
mまたはReferenceType::
mである場合、TypeNameまたはReferenceTypeが示す型がメソッド呼出しの修飾型です。式の形式がExpressionName
.
m、Primary.
m、ExpressionName::
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
を宣言され、値を戻さないことを示すものが含まれている必要があります。クラス・インスタンス作成式(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)へのシンボリック参照に解決する必要があります。コンストラクタのシグネチャには、次の両方が含まれている必要があります。
コンストラクタのパラメータ数
各仮パラメータの型へのシンボリック参照
クラスまたはインタフェースのバイナリ表現には、次もすべて含まれる必要があります。
これがクラスであり、
Object
ではない場合、このクラスの直接スーパークラスのイレイジャへのシンボリック参照。それぞれの直接スーパーインタフェースのイレイジャへのシンボリック参照(存在する場合)。
フィールドの単純名およびフィールドの型のイレイジャへのシンボリック参照として与えられた、クラスまたはインタフェース内で宣言された各フィールドの仕様。
これがクラスである場合、各コンストラクタの消去されたシグネチャ(前述を参照)。
クラスまたはインタフェース内で宣言された各メソッド(インタフェースについては、暗黙的に宣言されたメソッドを除く(9.2))ごとに、消去されたシグネチャおよび戻り型(前述を参照)。
クラスまたはインタフェースを実装するために必要なコード。
すべてのクラスまたはインタフェースに、その正規名(6.7)をリカバリするために十分な情報が含まれている必要があります。
すべてのメンバー・クラスまたはインタフェースに、ソースレベルのアクセス修飾子をリカバリするために十分な情報が含まれている必要があります。
すべてのネストしたクラスまたはインタフェースに、それを直接包含するクラスまたはインタフェース(8.1.3)へのシンボリック参照が含まれている必要があります。
すべてのクラスまたはインタフェースに、そのすべてのメンバー・クラスおよびインタフェース(8.5、9.5)へのシンボリック参照、およびその本体内で宣言されたネストした他のすべてのクラスおよびインタフェースへのシンボリック参照が含まれている必要があります。
Javaコンパイラによって発行されたコンストラクトは、発行されたコンストラクトがクラス初期化メソッドでないかぎり、ソース・コードで明示的または暗黙的に宣言されたコンストラクトに対応していない場合、合成としてマークする必要があります(JVMS §2.9)。
Javaコンパイラによって発行されたコンストラクトは、ソース・コードで暗黙的に宣言された仮パラメータに対応している場合、必須としてマークする必要があります(8.8.1、8.8.9、8.9.3、15.9.5.1)。
次の仮パラメータは、ソース・コードで暗黙的に宣言されます。
参考までに、次のコンストラクタは、ソース・コードでは暗黙的に宣言されますが、
class
ファイル内で必須としてマークできるのは仮パラメータのみであるため、必須としてはマークされません(JVMS §4.7.24)。
モジュール宣言に対応するclass
ファイルには、バイナリ名がmodule-info
であり、スーパークラス、スーパーインタフェース、フィールドおよびメソッドを持たないクラスのclass
ファイルのプロパティが必要です。また、このモジュールのバイナリ表現には次のものがすべて含まれている必要があります。
module
の後ろに示す名前へのシンボリック参照として与えられたモジュールの名前の仕様。また、仕様では、モジュールが標準とオープンのどちらであるかも示す必要があります(7.7)。requires
ディレクティブが示すモジュールの名前へのシンボリック参照として与えられ、このディレクティブによって示される各依存の仕様(7.7.1)。また、仕様では、依存がtransitive
であるかどうか、および依存がstatic
であるかどうかも示す必要があります。exports
またはopens
ディレクティブが示すパッケージの名前へのシンボリック参照として与えられ、これらのディレクティブが示す各パッケージの仕様(7.7.2)。また、ディレクティブが修飾されていた場合、仕様では、ディレクティブのto
句が示すモジュールの名前へのシンボリック参照を与える必要があります。uses
ディレクティブが示すクラスまたはインタフェースの名前へのシンボリック参照として指定された、このディレクティブが示す各サービスの仕様(7.7.3)。provides
ディレクティブのwith
句が示すクラスおよびインタフェースの名前へのシンボリック参照として指定された、このディレクティブが示すサービス・プロバイダの仕様(7.7.4)。また、仕様では、ディレクティブがサービスとして示すクラスまたはインタフェースの名前へのシンボリック参照を指定する必要があります。
次の各セクションでは、既存のバイナリとの互換性を損なわずにクラスおよびインタフェース宣言に対して加えられる可能性のある変更点について説明します。前述の翻訳要件に基づき、Java仮想マシンおよびそのclass
ファイル形式はこれらの変更をサポートしています。前述の要件に基づくクラス・ローダーによってclass
ファイルにマップし戻される圧縮または暗号化された表現など、他の有効なバイナリ形式もすべて、これらの変更を必然的にサポートします。
13.4 クラスの展開
13.4.27 レコード・クラスの展開
レコード宣言内のレコード・コンポーネントを追加、削除、変更または順序変更すると、既存のバイナリとの互換性が損なわれ、リンク時間のエラー、すなわちIncompatibleClassChangeError
が発生する可能性があります。
13.4.8および13.4.12で説明されているとおり、レコード・コンポーネントを削除すると、対応する暗黙のフィールド宣言および対応するアクセサ・メソッド宣言が削除され、標準コンストラクタおよび他のサポート・メソッドのシグネチャと実装も変更されます。
その他すべての点において、レコード・クラスのバイナリ互換性ルールは、標準クラスのルールと同じです。
第14章: ブロックおよび文
14.3 ローカル・クラス宣言
ローカル・クラスまたはローカル・インタフェースは、ネストしたクラスまたはインタフェース(8、9)であり、その宣言はブロックに直接含まれます(14.2)。
- LocalClassOrInterfaceDeclaration:
- ClassDeclaration
- EnumDeclaration
- RecordDeclaration
- InterfaceDeclaration
注釈インタフェース(9.6)はローカル・インタフェースとして宣言されない場合があります。
ローカル・クラスおよびインタフェース宣言は、ブロック内の文と自由に混在できます。
ローカル・クラスまたはインタフェースは、パッケージ、クラスまたはインタフェースのメンバーではありません。無名クラス(15.9.5)とは異なり、ローカル・クラスまたはインタフェースには単純名があります(6.2, 6.7)。
ローカルenumクラス、レコード・クラスおよびローカル・インタフェースは暗黙的にstatic
です(8.1.1.4、9.1.1.3)。暗黙的にstatic
でないローカル・クラスは内部クラスです(8.1.3)。
ローカル・クラスまたはインタフェースがpublic
、protected
または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
で修飾されたその単純名)が割当て演算子の左側に出現する場合にのみ出現出現するように定義されます。
これは、割当ての出現をイタリック表記にするための編集上の変更です。
...