このドキュメントでは、Java SE 21プレビュー機能である名前のないクラスおよび変数をサポートするためのJava言語仕様に対する変更について説明します。この機能の概要は、JEP:443を参照してください。
変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。
変更ログ:
2023-03-22: 初期仕様のドラフト。
第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つ以上の文字で構成された識別子には使用できますが、キーワード(3.9)であるために1文字の識別子としては使用できません。
「Javaの数字」には、ASCII数字
0-9
(\u0030-\u0039
)が含まれます。
文字および数字は、Unicode文字セット全体から取得できます。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
トークン化のルール(3.5)により、識別子には、予約キーワード(3.9)、ブール・リテラル(3.10.3)またはnullリテラル(3.10.8)と同じスペル(Unicode文字列)を使用することはできません。ただし、プログラム内でのシーケンスの表示場所によって、入力文字列を識別子またはコンテキスト・キーワードのどちらでトークン化するかは、プログラム内の順序の表示場所によって決まるため、識別子がコンテキスト・キーワードと同じスペルになる場合があります。
コンテキスト・キーワードの認識を促進するために、構文文法(2.3)では識別子のサブセットのみ受け入れるようプロダクションを定義し、特定の識別子を許可しない場合があります。サブセットは、次のとおりです。
- TypeIdentifier:
-
permits
、record
、sealed
、var
、またはyield
ではない識別子 - UnqualifiedMethodIdentifier:
-
yield
以外の識別子
TypeIdentifierはクラス、インタフェース、および型パラメータ(8.1、9.1、4.4)の宣言、および型への参照(6.5)に使用されます。たとえば、クラスの名前はTypeIdentifierである必要があるため、
permits
、record
、sealed
、var
またはyield
という名前のクラスを宣言することは不正です。
UnqualifiedMethodIdentifierは、メソッド呼出し式が単純名(6.5.7.1)でメソッドを参照する場合に使用されます。
yield
という用語はUnqualifiedMethodIdentifierから除外されるため、yield
という名前のメソッドの呼出しは、その呼出しをyield
文と区別するために修飾する必要があります(14.21)。
3.9 キーワード
次の変更は、JEP 440 (レコード・パターン)およびJEP 441 (switch
のパターン・マッチング)によるJLS変更が適用されていることを前提としています(JLS:JEP440+441)。
ASCII文字で構成された51個の文字シーケンスは、キーワードとして使用するために予約されており、識別子(3.8)として使用することはできません。その他の同じくASCII文字で構成された16個の文字シーケンスは、出現するコンテキストによっては、キーワードや別のトークンとして解釈される可能性があります。
- Keyword:
- ReservedKeyword
- ContextualKeyword
- ReservedKeyword:
- (次のうちの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
_
(アンダースコア) - ContextualKeyword:
- (次のうちの1つ)
-
exports permits sealed var
module provides to when
non-sealed record transitive with
open requires uses yield
opens
キーワード
const
およびgoto
は、現在使用されていませんが、予約されています。これにより、これらのC++のキーワードがプログラムで誤って出現した場合にJavaコンパイラがより適切なエラー・メッセージを作成できるようになります。
キーワード
strictfp
は廃止されています。新しいコードには使用しないでください。
キーワード
_
(アンダースコア)は、将来パラメータ宣言で使用できるように予約されています。
キーワード
_
(アンダースコア)は、識別子(6.1)のかわりに宣言で使用できます。
true
およびfalse
はキーワードではなく、booleanリテラルです(3.10.3)。
null
はキーワードではなく、nullリテラルです(3.10.8)。
入力文字から入力要素への還元(3.5)の際に、コンテキスト・キーワードと概念的に一致する入力文字のシーケンスは、次の両方の条件が満たされる場合にのみ、コンテキスト・キーワードに還元されます。
シーケンスは、次のように、構文文法(2.3)の適切なコンテキストで指定された終端として認識されます。
module
およびopen
については、ModuleDeclaration (7.7)で終端として認識される場合。exports
、opens
、provides
、requires
、to
、uses
およびwith
については、ModuleDirectiveで終端として認識される場合。transitive
については、RequiresModifierで終端として認識される場合。たとえば、シーケンス
requires
transitive
;
の認識では、RequiresModifierが使用されていないため、このtransitive
という用語はコンテキスト・キーワードではなく識別子に還元されます。var
については、LocalVariableType (14.4)またはLambdaParameterType (15.27.1)で終端として認識される場合。その他のコンテキストでは、識別子として
var
を使用しようとすると、var
はTypeIdentifier (3.8)ではないためにエラーが発生します。yield
については、YieldStatement (14.21)で終端として認識される場合。その他のコンテキストでは、識別子として
yield
を使用しようとすると、yield
はTypeIdentifierとUnqualifiedMethodIdentifierのどちらでもないためにエラーが発生します。record
については、RecordDeclaration (8.10)で終端として認識される場合。non-sealed
、permits
およびsealed
については、NormalClassDeclaration (8.1)またはNormalInterfaceDeclaration (9.1)で終端として認識される場合。when
については、Guard (14.11.1)で終端として認識される場合。
シーケンスの直前または直後に、JavaLetterOrDigitと一致する入力文字がない。
一般に、ソースコードで誤って空白を省略すると、「可能なかぎり長い変換」ルール(3.2)によって、入力文字のシーケンスが識別子としてトークン化されます。たとえば、12文字の入力文字シーケンス
p u b l i c s t a t i c
は、予約キーワードのpublic
およびstatic
としてではなく、常に識別子のpublicstatic
としてトークン化されます。2つのトークンを使用する場合は、それらを空白またはコメントで区切る必要があります。
前述のルールは、「可能なかぎり長い変換」ルールと連動して、コンテキスト・キーワードが現れるコンテキスト内で直感的な結果を生成します。たとえば、11文字の入力文字シーケンス
v a r f i l e n a m e
は、通常、識別子varfilename
としてトークン化されますが、ローカル変数宣言では、最初の3文字の入力文字は、前述のルールの最初の条件によって暫定的にコンテキスト・キーワードvar
として認識されます。ただし、シーケンス内の空白の不足を見落とすと、その次の8文字の入力文字を識別子filename
として認識することによる混乱が生じます。(これは、シーケンスが異なるコンテキストで異なるトークン化を経ることを意味します。ほとんどのコンテキストでは識別子ですが、ローカル変数宣言ではコンテキスト・キーワードと識別子です)。したがって、2番目の条件により、直後の入力文字のf
がJavaLetterOrDigitであるという理由で、コンテキスト・キーワードvar
の認識を防止します。そのため、シーケンスv a r f i l e n a m e
は、ローカル変数宣言の識別子varfilename
としてトークン化されます。
コンテキスト・キーワードの慎重な認識の別の例として、15文字の入力文字のシーケンス
n o n - s e a l e d c l a s s
について考えてみます。このシーケンスは、通常、識別子non
、演算子-
および識別子sealedclass
の3つのトークンに変換されますが、最初の条件が成立する通常のクラス宣言では、最初の10の入力文字は暫定的にコンテキスト・キーワードnon-sealed
として認識されます。シーケンスを3つの非キーワード・トークンではなく2つのキーワード・トークン(non-sealed
とclass
)に解釈されることを回避し、class
の前の空白を省略するプログラマに報酬を与えないようにするために、2番目の条件によってコンテキスト・キーワードの認識を防止します。そのため、シーケンスn o n - s e a l e d c l a s s
は、クラス宣言では3つのトークンとしてトークン化されます。
前述のルールでは、最初の条件は構文文法の詳細に依存しますが、Javaプログラミング言語のコンパイラは、入力プログラムを完全に解析することなくルールを実装できます。たとえば、コンテキスト・キーワードの有効な使用がキーワードとしてトークン化され、識別子の有効な使用が識別子としてトークン化されることがヒューリスティックで保証されているかぎり、ヒューリスティックを使用してトークナイザのコンテキスト状態を追跡できます。また、コンパイラでは常にコンテキスト・キーワードを識別子としてトークン化し、そうした識別子の特別な使用の認識は、それより後のフェーズに任せることもできます。
第6章: 名前
6.1 宣言
宣言により、エンティティがプログラムに導入され、このエンティティを参照するために名前内で使用できる識別子(3.8)が組み込まれます。この識別子には、導入されるエンティティがクラス、インタフェースまたは型パラメータである場合は特定のコンテキスト・キーワードを回避するという制約があります。
宣言されたエンティティは、次のいずれかです。
宣言は、次のいずれかのエンティティをプログラムに導入します。
module
宣言で宣言されたモジュール(7.7)package
宣言で宣言されたパッケージ(7.4)単一型インポート宣言またはオンデマンド型インポート宣言(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)
参照型のメンバー(8.2、9.2、8.9.3、9.6、10.7)で、次のいずれか:
フィールドで、次のいずれか:
メソッドで、次のいずれか:
enum定数(8.9.1)
レコード・コンポーネント(8.10.3)
仮パラメータで、次のいずれか:
try
文のcatch
句で宣言された例外ハンドラの例外パラメータ(14.20)ローカル変数で、次のいずれか:
ローカル・クラスまたはインタフェース(14.3)で、次のいずれか:
標準クラス宣言によって宣言されたローカル・クラス
enum宣言によって宣言されたローカル・クラス
レコード宣言によって宣言されたローカル・クラス
標準インタフェース宣言で宣言されたローカル・インタフェース
コンストラクタ(8.8、8.10.4)も宣言によって導入されますが、新しい名前が導入されるのではなく、そのコンストラクタが宣言されたクラスの名前が使用されます。
宣言には通常、エンティティを参照するために名前に使用できる識別子(3.8)が含まれます。この識別子には、導入されるエンティティがクラス、インタフェースまたは型パラメータである場合は特定のコンテキスト・キーワードを回避するという制約があります。
宣言に識別子が含まれず、かわりに予約キーワード_
(アンダースコア)が含まれている場合、エンティティを名前で参照することはできません。アンダースコアを使用して、次の種類のエンティティを宣言できます。
アンダースコアを使用して宣言されたローカル変数、例外パラメータ、またはラムダ・パラメータは、それぞれ名前のないローカル変数、名前のない例外パラメータ、名前のないラムダ・パラメータと呼ばれます。
汎用クラスまたはインタフェースの宣言(
class
C<
T>
...
またはinterface
C<
T>
...
)は、Cという名前のクラスおよび型のファミリ: RAW型C、パラメータ化型C<Foo>
、パラメータ化型C<Bar>
などの両方を導入します。
非汎用コンテキストの1つとして次に示されている汎用性が重要ではない場所でCへの参照が発生した場合、Cへの参照は、クラスまたはインタフェースCを示します。その他のコンテキストでは、Cへの参照は、Cによって導入された型または型の一部を示します。
15種類の非汎用コンテキストは、次のとおりです。
モジュール宣言内の
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)例外パラメータ宣言内(14.20)
最初の12の非汎用コンテキストは、[6.5.1]内のTypeNameの最初の12の構文的コンテキストに対応しています。13番目の非汎用コンテキストは、修飾されたExpressionName (
C.x
など)に、静的メンバー・アクセスを示すためのTypeNameC
が含まれる可能性がある場所です。これらの13のコンテキスト内でTypeNameが共通して使用されていることが重要です。これは、これらのコンテキストには、型のファーストクラス未満の使用方法が含まれることを示します。対照的に、14番目と15番目の非汎用コンテキストではClassTypeが採用されています。これは、throws
およびcatch
句には、型がファーストクラスの方法でフィールド宣言などにあわせて使用されていることを示します。これら2つのコンテキストに非汎用としての特性が与えられるのは、例外型をパラメータ化できないという事実のためです(8.1.2)
ClassTypeプロダクションでは注釈が許可されるため、
throws
またはcatch
句内で型の使用に注釈を付けることができます。一方、TypeNameプロダクションでは注釈が許可されないため、単一型インポート宣言などの型の名前に注釈を付けることはできません。
命名規則
Java SEプラットフォームのクラス・ライブラリは、可能なかぎり、次の命名規則に従って選択された名前を使用しようとします。これらの命名規則は、コードをより読みやすくし、ある種の命名規則の競合を回避する上で役に立ちます。
Javaプログラミング言語で書かれたすべてのプログラムで、これらの命名規則を使用することをお薦めします。ただし、以前から使用されてきた慣例的な使用方法とは異なる場合、これらの命名規則に無条件に従う必要はありません。このため、たとえば、クラス
java.lang.Math
のsin
およびcos
メソッドには、ここで提案されている命名規則をこれらのメソッド名が無視する形になるとしても、これらは短く、動詞ではないため、数学的に慣例的な名前が使用されます。
パッケージ名およびモジュール名
プログラマは、広範に配布するパッケージに一意のパッケージ名を選択することにより、公開される2つのパッケージが同じ名前を持つ可能性を避けるべく策を講じる必要があります。これにより、パッケージを簡単かつ自動的にインストールおよびカタログ化できるようになります。このセクションでは、このような一意のパッケージ名を生成するために提案されている命名規則を規定します。一連のパッケージをローカルかつ日常的なパッケージ名から、ここで説明する一意の名前形式に変換する処理を自動的にサポートするために、Java SEプラットフォームを実装することをお薦めします。
一意のパッケージ名を使用しない場合、競合するパッケージの作成とはまったく異なる点でパッケージ名の競合が発生する可能性があります。この結果、ユーザーやプログラマでは解決が困難または不可能になる状況が生じるおそれがあります。クラス
ClassLoader
およびModuleLayer
を使用して、同じ名前を持つパッケージを相互に隔離できますが、その場合、パッケージのインタラクションが制限されても、単純なプログラムには透過的な方法では制限されません。
一意のパッケージ名を形成するには、最初に
oracle.com
などのインターネット・ドメイン名を用意します(またはこのようなドメイン名を持つ組織に属します)。その後、この名前をコンポーネント別に反転させて、この例ではcom.oracle
を取得します。次に、組織内でパッケージ名をより詳細に管理するために策定した命名規則を使用して、この名前をパッケージ名の接頭辞として使用します。このような命名規則では、特定のパッケージ名コンポーネントを部署、部門、プロジェクト、マシンまたはログイン名として指定する場合があります。
例6.1-1.一意のパッケージ名
com.nighthacks.scrabble.dictionary
org.openjdk.compiler.source.tree
net.jcip.annotations
edu.cmu.cs.bovik.cheese
gov.whitehouse.socks.mousefinder
一意のパッケージ名の最初のコンポーネントは常に、すべて小文字のASCII文字で書かれます。これは、
com
、edu
、gov
、mil
、net
またはorg
などの最上位のドメイン名の1つ、またはISO標準3166に規定されている国を示す英語2文字のコードの1つにする必要があります。
場合によっては、インターネットのドメイン名が有効なパッケージ名ではないことがあります。次に、これらの状況に対処する上で提案される命名規則を示します。
モジュールの名前は、エクスポートされたプリンシパル・パッケージの名前に対応している必要があります。モジュールにこのようなパッケージがない場合、または慣習的な理由により、モジュール名をエクスポートされたパッケージの1つに対応していない名前にする必要がある場合、その名前は依然として、その作者が関連するインターネットのドメインを反転させた形から始まる必要があります。
例6.1-2.一意のモジュール名
com.nighthacks.scrabble
org.openjdk.compiler
net.jcip.annotations
パッケージまたはモジュール名の最初のコンポーネントは、識別子
java
にしないでください。識別子java
から始まるパッケージまたはモジュール名は、Java SEプラットフォームのパッケージまたはモジュール用として予約されています。
パッケージまたはモジュールの名前は、パッケージまたはモジュールがインターネット上のどこに格納されているかを示すことを意図していません。たとえば、
edu.cmu.cs.bovik.cheese
という名前のパッケージは必ずしも、ホストcmu.edu
またはcs.cmu.edu
またはbovik.cs.cmu.edu
から取得できるわけではありません。一意のパッケージまたはモジュール名を生成するために提案される命名規則は、パッケージまたはモジュール名用として別のレジストリを作成するかわりに、広く知られる既存の一意の名前レジストリにパッケージおよびモジュールの命名規則を単に便乗させる方法です。
クラス名とインタフェース名
クラスの名前は、過度に長くない説明的な名詞または名詞句にし、大文字と小文字を混在させ、各単語の先頭を大文字にする必要があります。
例6.1-3.説明的なクラス名
`ClassLoader`
SecurityManager
`Thread`
Dictionary
BufferedInputStream
同様に、インタフェースの名前は、過度に長くない短い説明的な名前にし、大文字と小文字を混在させ、各単語の先頭を大文字にする必要があります。この名前は、説明的な名詞または名詞句である場合があります。これは、インタフェースが抽象的なスーパークラスであるかのように使用される場合(
java.io.DataInput
やjava.io.DataOutput
など)、またはインタフェースが動作を示す形容詞である場合(インタフェースRunnable
やCloneable
など)である場合に適しています。
型変数名
型変数名は、簡潔(可能であれば1文字)でありながら示唆に富む名前にする必要があります。また、小文字は含めないでください。これにより、型パラメータを一般的なクラスやインタフェースと区別しやすくなります。
コンテナ・クラスおよびインタフェースには、要素型として名前
E
を使用する必要があります。マップには、キーの型としてK
、および値の型としてV
を使用する必要があります。任意の例外型には、名前X
を使用する必要があります。Oracleでは、型を区別するためにより具体的な名前がない場合は、型にT
を使用しています。(これはたいてい、汎用メソッドの場合です。)
任意の型を示す型パラメータが複数存在する場合、アルファベットで
T
の隣にある文字(S
など)を使用する必要があります。また、異なる型変数を区別するために下付き数字(T1
、T2
など)を使用することも許容されます。このような場合、同じ接頭辞を持つすべての変数に添字を付ける必要があります。
汎用クラス内に汎用メソッドが出現する場合、混乱を避けるために、メソッドおよびクラスの型パラメータに同じ名前を使用しないようにすることをお薦めします。同じことが、ネストした汎用クラスにも当てはまります。
例6.1-4.従来の型変数名
public class HashSet<E> extends AbstractSet<E> { ... }
public class HashMap<K,V> extends AbstractMap<K,V> { ... }
public class ThreadLocal<T> { ... }
public interface Functor<T, X extends Throwable> {
T eval() throws X;
}
型パラメータが前述のカテゴリの1つに都合よく該当しない場合、名前はできるだけ1文字の範囲内で意味のあるものとして選択する必要があります。前述の名前(
E
、K
、V
、X
、T
)は、指定したカテゴリに該当しない型パラメータには使用しないでください。
メソッド名
メソッド名は、動詞または動詞句にし、大文字と小文字を混在させ、先頭の文字を小文字にし、後続の任意の単語の先頭を大文字にする必要があります。次に、メソッド名に関する追加の特定の命名規則を示します。
変数Vであるとみなされる可能性がある属性を取得および設定するメソッドには、
getV
およびsetV
という名前を付ける必要があります。例として、クラスThread
のメソッドgetPriority
およびsetPriority
があります。何かの長さを返すメソッドには、クラス
String
内の場合のように、length
という名前を付ける必要があります。オブジェクトに関するブール条件Vをテストするメソッドには、
isV
という名前を付ける必要があります。例として、クラスThread
のメソッドisInterrupted
があります。オブジェクトを特定のフォーマットFに変換するメソッドには、
toF
という名前を付ける必要があります。例として、クラスObject
のメソッドtoString
、クラスjava.util.Date
のメソッドtoLocaleString
およびtoGMTString
があります。
可能かつ適切であるかぎり、新しいクラス内のメソッドの名前を同様の既存のクラス(特にJava SEプラットフォームAPIのクラス)に基づいて付けると、使用しやすくなります。
フィールド名
final
ではないフィールドの名前は、大文字と小文字を混在させ、先頭の文字を小文字にし、後続の単語の先頭を大文字にする必要があります。適切に設計されたクラスの場合、定数(static
またはfinal
フィールド)であるフィールドは除き、public
またはprotected
フィールドが使用されることはほとんどありません。
フィールドには、名詞、名詞句または名詞の短縮形である名前が含まれる必要があります。
この命名規則の例として、クラス
java.io.ByteArrayInputStream
のフィールドbuf
、pos
およびcount
、およびクラスjava.io.InterruptedIOException
のフィールドbytesTransferred
があります。
定数名
インタフェース内の定数の名前は、1つ以上の単語、頭字語または短縮形のシーケンスにし、すべて大文字にし、コンポーネントをアンダースコア「
_
」文字で区切る必要があります。また、クラスの変数final
の場合も、慣習的にこのようにする場合があります。定数名は説明的なものにし、不必要に省略しないでください。これらは慣習的に、妥当な話し言葉の一部である場合があります。
定数名の例として、クラス
Character
のMIN_VALUE
、MAX_VALUE
、MIN_RADIX
およびMAX_RADIX
があります。
セットの代替値、またはそれほど頻繁ではありませんが整数値のマスキング・ビットを表す定数のグループは、場合によっては名前の接頭辞として共通の頭字語を使用して指定すると役に立ちます。
たとえば、次のようになります。
interface ProcessStates { int PS_RUNNING = 0; int PS_SUSPENDED = 1; }
ローカル変数およびパラメータ名
ローカル変数およびパラメータ名は短いながら意味のあるものにする必要があります。これらは多くの場合、次のような単語ではない小文字の短いシーケンスです。
頭字語、
ColoredPoint
への参照が格納された変数のcp
の場合のように、一連の単語の先頭文字です短縮形、ある種のバッファへのポインタが格納された変数の
buf
などですニーモニック用語、通常は広く使用されているクラスのパラメータ名の後ろでパターン化された慣例的な名前を持つ一連のローカル変数を使用して、記憶しやすく、わかりやすい方法で編成されています。たとえば、次のようになります。
in
およびout
、ある種の入力および出力が関わる場合は常に、System
のフィールドの後ろでパターン化されます
off
およびlen
、オフセットおよび長さが関わる場合は常に、java.io
のインタフェースDataInput
およびDataOutput
のメソッドread
およびwrite
のパラメータの後ろでパターン化されます
1文字のローカル変数またはパラメータ名は回避する必要があります。ただし、一時またはループ変数の場合、あるいは変数に特徴のない型の値が格納されている場合は除きます。慣例的な1文字の名前は、次のとおりです。
byte
を表すb
char
を表すc
double
を表すd
Exception
を表すe
float
を表すf
int
を表すi
、j
およびk
long
を表すl
Object
を表すo
String
を表すs
- ある型の任意の値を表す
v
2、3の小文字のみで構成されたローカル変数またはパラメータ名は、一意のパッケージ名の最初のコンポーネントである最初の国コードおよびドメイン名と競合しないようにする必要があります。
第8章: クラス
8.3 フィールド宣言
クラスの変数は、フィールド宣言によって導入されます。
- FieldDeclaration:
-
{FieldModifier} UnannType VariableDeclaratorList
;
- VariableDeclaratorList:
-
VariableDeclarator {
,
VariableDeclarator} - VariableDeclarator:
-
VariableDeclaratorId [
=
VariableInitializer]
- VariableDeclaratorId:
- Identifier [Dims]
- VariableDeclaratorId:
- Identifier [Dims]
-
_
- VariableInitializer:
- Expression
- ArrayInitializer
- UnannType:
- UnannPrimitiveType
- UnannReferenceType
- UnannPrimitiveType:
- NumericType
-
boolean
- UnannReferenceType:
- UnannClassOrInterfaceType
- UnannTypeVariable
- UnannArrayType
- UnannClassOrInterfaceType:
- UnannClassType
- UnannInterfaceType
- UnannClassType:
- TypeIdentifier [TypeArguments]
-
PackageName
.
{Annotation} TypeIdentifier [TypeArguments] -
UnannClassOrInterfaceType
.
{Annotation} TypeIdentifier
[TypeArguments] - UnannInterfaceType:
- UnannClassType
- UnannTypeVariable:
- TypeIdentifier
- UnannArrayType:
- UnannPrimitiveType Dims
- UnannClassOrInterfaceType Dims
- UnannTypeVariable Dims
便宜上、ここでは4.3の次のプロダクションを示します。
- Dims:
- {Annotation}
[
]
{{Annotation}[
]
}
FieldDeclaration内の各宣言子は、それぞれ1つのフィールドを宣言します。宣言子には識別子を含める必要があります。そうでない場合、コンパイル時エラーが発生します。宣言子内の識別子は、フィールドを参照する名前に使用できます。
複数の宣言子を使用して、1つのFieldDeclarationで複数のフィールドを宣言できます。複数のFieldModifierとUnannTypeは、宣言内のすべての宣言子に適用されます。
FieldModifier句については、[8.3.1]を参照してください。
UnannTypeとVariableDeclaratorIdに角カッコのペアがない場合は、宣言されるフィールドの型はUnannTypeによって指定され、そうでない場合は10.2で規定されています。
フィールド宣言のスコープおよびシャドウ化については、6.3および6.4.1に規定されています。
クラス宣言の本体で、同じ名前のフィールドを複数宣言すると、コンパイル時にエラーが発生します。
クラスが特定の名前のフィールドを宣言する場合、そのフィールドの宣言は、クラスのスーパークラスおよびスーパーインタフェース内の同じ名前のフィールドのすべてのアクセス可能な宣言を隠すと言うことができます。
この点において、フィールドの非表示は、メソッドの非表示(8.4.8.3)とは異なります。フィールド非表示での
static
フィールドと非static
フィールドとの間は区別されず、メソッド非表示でのstatic
メソッドと非static
メソッドとの間は区別されます。
非表示にされるフィールドには、static
の場合は修飾名(6.5.6.2)を使用するか、キーワードsuper
(15.11.2)またはスーパークラス型へのキャストを含むフィールド・アクセス式を使用してアクセスできます。
この点に関しては、フィールドの非表示はメソッドの非表示と同様です。
フィールド宣言によって別のフィールドの宣言が非表示になる場合、2つのフィールドの型が同じである必要はありません。
クラスは、その直接スーパークラスおよび直接スーパーインタフェースから、スーパークラスおよびスーパーインタフェースの非private
フィールドのうち、クラス内のコードにアクセス可能(6.6)で、かつクラス内の宣言によって隠されないすべてのものを継承します。
スーパークラスとサブクラスの両方が同じクラスのメンバーである場合などは、スーパークラスのprivate
フィールドにサブクラスからアクセスできます。ただし、private
フィールドがサブクラスに継承されることはありません。
クラスは、そのスーパークラスとスーパーインタフェース、またはそのスーパーインタフェースのみから、同じ名前の複数のフィールドを継承できます。そのような状況自体によってコンパイル時にエラーが発生することはありません。ただし、クラスの本体内でそのようなフィールドを単純名で参照しようとすると、参照があいまいなため、コンパイル時にエラーが発生します。
インタフェースから同じフィールドの宣言が継承されるパスが複数ある場合があります。このような状況下では、フィールドは1回のみ継承されたとみなされ、あいまいさなしに単純名で参照できます。
例8.3-1.継承フィールドの乗算
クラスは、スーパークラスおよびスーパーインタフェースから、または2つのスーパーインタフェースから、同じ名前の2つ以上のフィールドを継承できます。単純名によってあいまいな継承フィールドを参照しようとすると、コンパイル時エラーが発生します。キーワードsuper
(15.11.2)を含む修飾名またはフィールド・アクセス式を使用して、このようなフィールドに明確にアクセスできます。プログラム内では次のようになります。
interface Frob { float v = 2.0f; }
class SuperTest { int v = 3; }
class Test extends SuperTest implements Frob {
public static void main(String[] args) {
new Test().printV();
}
void printV() { System.out.println(v); }
}
クラスTest
は、v
という名前のフィールドをそれぞれスーパークラスSuperTest
から1つ、スーパーインタフェースFrob
から1つ継承します。これ自体は許可されますが、メソッドprintV
で単純名v
が使用されているためコンパイル時エラーが発生します。ここでは、どのv
を意味するかを判断できません。
次のバリエーションでは、フィールド・アクセス式super.v
を使用して、クラスSuperTest
で宣言されたv
というフィールドを参照し、修飾名Frob.v
を使用して、インタフェースFrob
で宣言されたv
というフィールドを参照します。
interface Frob { float v = 2.0f; }
class SuperTest { int v = 3; }
class Test extends SuperTest implements Frob {
public static void main(String[] args) {
new Test().printV();
}
void printV() {
System.out.println((super.v + Frob.v)/2);
}
}
次のようにコンパイルおよび出力されます。
2.5
2つの異なる継承フィールドのタイプと値が同じで、どちらもfinal
である場合でも、単純名によるいずれかのフィールドへの参照はあいまいとみなされ、コンパイル時エラーになります。プログラム内では次のようになります。
interface Color { int RED=0, GREEN=1, BLUE=2; }
interface TrafficLight { int RED=0, YELLOW=1, GREEN=2; }
class Test implements Color, TrafficLight {
public static void main(String[] args) {
System.out.println(GREEN); // compile-time error
System.out.println(RED); // compile-time error
}
}
クラスTest
は異なる値を持つGREEN
で2つの異なる宣言を継承するため、当然ながら、GREEN
への参照があいまいであるとみなされます。この例のポイントは、2つの異なる宣言が継承されるため、RED
への参照もあいまいであるとみなされることです。RED
という名前の2つのフィールドのタイプが同じで、値も同じまま変わらない場合、この判断には反映されません。
例8.3-2.フィールドの再継承
同じフィールド宣言が複数のパスを経由してインタフェースから継承される場合、フィールドは1回のみ継承されるとみなされます。これは、あいまいさのない単純な名前で参照できます。たとえば、次のコードのようになります。
interface Colorable {
int RED = 0xff0000, GREEN = 0x00ff00, BLUE = 0x0000ff;
}
interface Paintable extends Colorable {
int MATTE = 0, GLOSSY = 1;
}
class Point { int x, y; }
class ColoredPoint extends Point implements Colorable {}
class PaintedPoint extends ColoredPoint implements Paintable {
int p = RED;
}
フィールドRED
、GREEN
およびBLUE
は、ダイレクト・スーパークラスColoredPoint
とそのダイレクト・スーパーインタフェースPaintable
を介して、クラスPaintedPoint
によって継承されます。単純名RED
、GREEN
およびBLUE
は、インタフェースColorable
で宣言されたフィールドを参照するため、クラスPaintedPoint
内であいまいさのない状態で使用できます。
8.4 メソッド宣言
8.4.1 仮パラメータ
メソッドまたはコンストラクタの仮パラメータがある場合、カンマ区切りパラメータ指定子のリストで指定されます。パラメータ指定子はそれぞれ、型(オプションでその前にfinal
修飾子および/または1つ以上の注釈が付きます)、およびパラメータの名前を指定する識別子(オプションで角カッコが続きます)で構成されます。
メソッドまたはコンストラクタに仮パラメータもレシーバ・パラメータもない場合、メソッドまたコンストラクタの宣言に空のカッコのペアが出現します。
- FormalParameterList:
-
FormalParameter {
,
FormalParameter} - FormalParameter:
- {VariableModifier} UnannType VariableDeclaratorId
- VariableArityParameter
- VariableArityParameter:
-
{VariableModifier} UnannType {Annotation}
...
Identifier - VariableModifier:
- Annotation
-
final
- VariableDeclaratorId:
- Identifier [Dims]
- VariableDeclaratorId:
- Identifier [Dims]
- _
- Dims:
- {Annotation}
[
]
{{Annotation}[
]
}
メソッドまたはコンストラクタの仮パラメータは、型に続く省略記号が示す可変個引数パラメータである場合があります。1つのメソッドまたはコンストラクタに対して最大で1つの可変個引数パラメータが許可されています。可変個引数パラメータがパラメータ指定子のリスト内で最後の位置以外の場所に出現する場合は、コンパイル時にエラーが発生します。
VariableArityParameterの文法について、省略記号(
...
)はそれ自体へのトークンであることに注意してください(3.11)。それと型の間に空白を入れることは可能ですが、スタイルの面からお薦めできません。
メソッドの最後の仮パラメータが可変個引数パラメータの場合、メソッドは可変個引数メソッドになります。そうでない場合は固定個引数メソッドです。
仮パラメータ宣言およびレシーバ・パラメータに対する注釈修飾子のルールは、9.7.4および9.7.5に規定されています。
final
が仮パラメータ宣言の修飾子に複数回出現する場合、コンパイル時にエラーが発生します。
仮パラメータのスコープおよびシャドウ化については、6.3および6.4に規定されています。
ネストされたクラス、インタフェース、またはラムダ式から仮パラメータへの参照は、6.5.6.1に規定のとおり制限されています。
メソッドまたはコンストラクタの仮パラメータのすべての宣言に識別子が含まれている必要があります。そうでない場合、コンパイル時にエラーが発生します。
メソッドまたはコンストラクタで同じ名前を持つ2つの仮パラメータを宣言する場合は、コンパイル時にエラーが発生します。(つまり、その宣言は同じ識別子を指定しています。)
final
として宣言された仮パラメータがメソッドまたはコンストラクタの本体内に割り当てられると、コンパイル時にエラーが発生します。
仮パラメータの宣言型は、それが可変個引数パラメータかどうかによって決まります。
仮パラメータが可変個引数パラメータでない場合、宣言される型はUnannTypeとVariableDeclaratorIdに角カッコのペアがなければUnannTypeで示され、そうでない場合は10.2で規定されています。
仮パラメータが可変個引数パラメータの場合、宣言される型は10.2で規定されている配列型です。
可変個引数パラメータの宣言される型に非具象化可能な要素型(4.7)がある場合、可変個引数メソッドに@SafeVarargs
(9.6.4.7)で注釈が付けられていないかぎりはそのメソッドの宣言にコンパイル時に警告がチェックされないか、警告が@SuppressWarnings
(9.6.4.5)によって非表示にされます。
メソッドまたはコンストラクタが呼び出されると(15.12)、メソッドまたはコンストラクタの本体の実行前に、それぞれ宣言された型である、新しく作成されたパラメータ変数が実引数式の値によって初期化されます。FormalParameterに出現する識別子は、メソッドまたはコンストラクタの本体で、仮パラメータへの参照のための単純名として使用できます。
可変個引数メソッドの呼出しには、仮パラメータより多くの実引数式が含まれることがあります。可変個引数パラメータの前の仮パラメータに対応しない実引数式はすべて評価され、その結果が配列に格納され、メソッド呼出しに渡されます(15.12.4.2)。
インスタンス・メソッド内および内部クラスのコンストラクタ内のレシーバ・パラメータの例の一部を次に示します。
class Test { Test(/* ?? ?? */) {} // No receiver parameter is permitted in the constructor of // a top level class, as there is no conceivable type or name. void m(Test this) {} // OK: receiver parameter in an instance method static void n(Test this) {} // Illegal: receiver parameter in a static method class A { A(Test Test.this) {} // OK: the receiver parameter represents the instance // of Test which immediately encloses the instance // of A being constructed. void m(A this) {} // OK: the receiver parameter represents the instance // of A for which A.m() is invoked. class B { B(Test.A A.this) {} // OK: the receiver parameter represents the instance // of A which immediately encloses the instance of B // being constructed. void m(Test.A.B this) {} // OK: the receiver parameter represents the instance // of B for which B.m() is invoked. } } }
B
のコンストラクタとインスタンス・メソッドは、レシーバ・パラメータの型が他の型と同様に、修飾子TypeNameで示されていますが、内部クラスのコンストラクタ内のレシーバ・パラメータの名前は、包含クラスの単純名を使用する必要があることを示しています。
8.10 レコード・クラス
8.10.1 レコード・コンポーネント
レコード・クラスのレコード・コンポーネントが存在する場合は、レコード宣言のヘッダーで指定されます。各レコード・コンポーネントは、型(オプションで、その前に1つ以上の注釈が付く)およびレコード・コンポーネントの名前を指定する識別子で構成されます。レコード・コンポーネントは、レコード・クラスの2つのメンバーに対応します。1つは、暗黙的に宣言されたprivate
フィールド、もう1つは、明示的にまたは暗黙的に宣言されたpublic
アクセッサ・メソッドです(8.10.3)。
レコード・クラスにレコード・コンポーネントがない場合、レコード宣言のヘッダーに空のカッコのペアが表示されます。
- RecordHeader:
-
(
[RecordComponentList])
- RecordComponentList:
-
RecordComponent {
,
RecordComponent} - RecordComponent:
- {RecordComponentModifier} UnannType Identifier
- VariableArityRecordComponent
- VariableArityRecordComponent:
-
{RecordComponentModifier} UnannType {Annotation}
...
Identifier - RecordComponentModifier:
- Annotation
レコード・コンポーネントは、型に続く省略記号が示す可変個引数レコード・コンポーネントである場合があります。1つのレコード・クラスに対して最大で1つの可変個引数レコード・コンポーネントが許可されています。可変個引数レコード・コンポーネントがレコード・コンポーネントのリスト内で最後の位置以外の場所に出現する場合は、コンパイル時にエラーが発生します。
レコード・コンポーネント宣言の注釈修飾子に関するルールについては、9.7.4および9.7.5に規定されています。
レコード・コンポーネント宣言の注釈は、その注釈インタフェースがレコード・コンポーネント・コンテキスト(9.6.4.1)に適用可能な場合は、リフレクションを介して使用できます。注釈インタフェースが他のコンテキスト(8.10.3、8.10.4)で適用可能な場合、レコード・コンポーネント宣言の注釈は個別にレコード・クラスのメンバーおよびコンストラクタの宣言に伝播されます。
レコード宣言で名前clone
、finalize
、getClass
、hashCode
、notify
、notifyAll
、toString
またはwait
を持つレコード・コンポーネントを宣言する場合は、コンパイル時にエラーが発生します。
これらは、
Object
の引数なしのpublic
およびprotected
メソッドの名前です。これらをレコード・コンポーネントの名前に禁止すると、様々な形で混乱を回避できます。まず、すべてのレコード・クラスは、レコード・オブジェクト全体の表現を返すhashCode
およびtoString
の実装を提供しますが、hashCode
またはtoString
と呼ばれるレコード・コンポーネントのアクセッサ・メソッド(8.10.3)としては使用できず、レコード・クラス外部からこのようなレコード・コンポーネントにアクセスする方法はありません。同様に、一部のレコード・クラスでは、clone
および(特に)finalize
の実装が提供される可能性があるため、clone
またはfinalize
と呼ばれるレコード・コンポーネントにはアクセッサ・メソッドではアクセスできませんでした。最後に、Object
のgetClass
、notify
、notifyAll
およびwait
メソッドはfinal
であるため、同じ名前のレコード・コンポーネントにはアクセッサ・メソッドを使用できませんでした。(アクセッサ・メソッドのシグネチャはfinal
メソッドと同じであるため、オーバーライドしようとしても失敗します。)
レコード宣言で同じ名前を持つ2つのレコード・コンポーネントを宣言する場合は、コンパイル時にエラーが発生します。
レコード宣言のレコード・コンポーネントのすべての宣言に識別子が含まれている必要があります。そうでない場合、コンパイル時にエラーが発生します。
宣言されるレコード・コンポーネントの型は、それが可変個引数レコード・コンポーネントであるかどうかによって異なります。
レコード・コンポーネントが可変個引数レコード・コンポーネントでない場合、宣言された型はUnannTypeで示されます。
レコード・コンポーネントが可変個引数レコード・コンポーネントである場合、宣言される型は10.2に規定されている配列型です。
宣言された可変個引数レコード・コンポーネントの型に型情報保持可能でない要素型(4.7)が含まれる場合に、標準コンストラクタ(8.10.4)に@SafeVarargs
(9.6.4.7)の注釈が付けられていないか、または警告が@SuppressWarnings
(9.6.4.5)によって抑制されていないと、コンパイル時に可変個引数レコード・コンポーネントの宣言に対して未チェック警告が発生します。
第9章 インタフェース
9.3 フィールド(定数)宣言
- ConstantDeclaration:
-
{ConstantModifier} UnannType VariableDeclaratorList
;
- ConstantModifier:
- (次のうちの1つ)
-
Annotation
public
-
static
final
UnannTypeについては、8.3を参照してください。便宜上、ここでは4.3および8.3の次のプロダクションを示します。
- VariableDeclaratorList:
- VariableDeclarator {
,
VariableDeclarator}- VariableDeclarator:
- VariableDeclaratorId [
=
VariableInitializer]
- VariableDeclaratorId:
- Identifier [Dims]
- VariableDeclaratorId:
- Identifier [Dims]
- _
- Dims:
- {Annotation}
[
]
{{Annotation}[
]
}- VariableInitializer:
- Expression
- ArrayInitializer
インタフェース・フィールド宣言の注釈修飾子に関するルールについては、9.7.4および9.7.5に規定されています。
インタフェース宣言の本体のすべてのフィールド宣言は、暗黙的にpublic
、static
およびfinal
です。このようなフィールドには、これらの修飾子のいずれかまたはすべての重複指定が許可されています。
フィールド宣言について同じキーワードが修飾子として複数回出現する場合、コンパイル時にエラーが発生します。
1つのフィールド宣言に(個別)フィールド修飾子が複数ある場合、慣例ではConstantModifierのプロダクションで前述したものと一致する順序で表示されますが、必須ではありません。
UnannTypeとVariableDeclaratorIdに角カッコのペアがない場合は、宣言されるフィールドの型はUnannTypeによって指定され、そうでない場合は10.2で規定されています。
ConstantDeclaration内のすべての宣言子にIdentifierを含める必要があります。そうでない場合、コンパイル時にエラーが発生します。
インタフェース・フィールド宣言のスコープおよびシャドウ化については、6.3および6.4.1に規定されています。
インタフェース・フィールドはstatic
であるため、その宣言では静的コンテキスト(8.1.3)が導入され、現在のオブジェクトを参照する構成の使用が制限されます。特に、キーワードthis
およびsuper
は、静的コンテキスト(15.8.3、15.11.2)では禁止されています。これは、字句として囲んでいる宣言(6.5.5.1、6.5.6.1、15.12.3)のインスタンス変数、インスタンス・メソッドおよび型パラメータに対する参照が修飾されていないためです。
インタフェース宣言の本体で、同じ名前のフィールドを複数宣言すると、コンパイル時にエラーが発生します。
インタフェースが特定の名前のフィールドを宣言する場合、そのフィールドの宣言は、インタフェースのスーパーインタフェース内で同じ名前のフィールドのすべてのアクセス可能な宣言を隠すと言うことができます。
インタフェースは、同じ名前の複数のフィールドを継承できます。そのような状況自体によってコンパイル時にエラーが発生することはありません。ただし、インタフェース宣言の本体内でそのようなフィールドを単純名で参照しようとすると、参照があいまいなため、コンパイル時にエラーが発生します。
インタフェースから同じフィールドの宣言が継承されるパスが複数ある場合があります。このような状況下では、フィールドは1回のみ継承されたとみなされ、あいまいさなしに単純名で参照できます。
例9.3-1.あいまいな継承フィールド
たとえば、2つの直接スーパーインタフェースがその名前でフィールドを宣言するため、同じ名前の2つのフィールドが1つのインタフェースに継承されると、単一のあいまいなメンバー結果が生じます。このあいまいなメンバーを使用すると、コンパイル時にエラーが発生します。プログラム内では次のようになります。
interface BaseColors {
int RED = 1, GREEN = 2, BLUE = 4;
}
interface RainbowColors extends BaseColors {
int YELLOW = 3, ORANGE = 5, INDIGO = 6, VIOLET = 7;
}
interface PrintColors extends BaseColors {
int YELLOW = 8, CYAN = 16, MAGENTA = 32;
}
interface LotsOfColors extends RainbowColors, PrintColors {
int FUCHSIA = 17, VERMILION = 43, CHARTREUSE = RED+90;
}
インタフェースLotsOfColors
は、YELLOW
という名前の2つのフィールドを継承します。これは、単純名によるフィールドYELLOW
への参照がインタフェースに含まれていないかぎり有効です。(このような参照は、フィールドの変数イニシャライザ内で発生する可能性があります。)
インタフェースPrintColors
が値8
ではなく値3
をYELLOW
に指定する場合でも、インタフェースLotsOfColors
内でのフィールドYELLOW
への参照はあいまいとみなされます。
例9.3-2.継承フィールドの乗算
たとえば、このインタフェースとこのインタフェースの直接スーパーインタフェースの両方がフィールドを宣言するインタフェースを拡張するなど、単一のフィールドが同じインタフェースから複数回継承される場合、単一のメンバー結果だけが生じます。このような状況自体によってコンパイル時にエラーが発生することはありません。
前述の例では、フィールドRED
、GREEN
およびBLUE
は、インタフェースRainbowColors
およびインタフェースPrintColors
を介して、インタフェースLotsOfColors
によって複数の方法で継承されますが、インタフェースLotsOfColors
のフィールドRED
への参照は、フィールドRED
の実際の宣言が1つだけであるため、あいまいとはみなされません。
第14章: ブロックと文およびパターン
次の変更は、JEP 440 (レコード・パターン)およびJEP 441 (switch
のパターン・マッチング)によるJLS変更が適用されていることを前提としています(JLS:JEP440+441)。
14.4 ローカル変数宣言
ローカル変数宣言では、1つ以上のローカル変数を宣言し、オプションで初期化します(4.12.3)。
- LocalVariableDeclaration:
- {VariableModifier} LocalVariableType VariableDeclaratorList
- LocalVariableType:
- UnannType
-
var
UnannTypeについては、8.3を参照してください。便宜上、4.3、8.3および8.4.1からの次のプロダクションをここに示します。
- VariableModifier:
- Annotation
final
- VariableDeclaratorList:
- VariableDeclarator {
,
VariableDeclarator}- VariableDeclarator:
- VariableDeclaratorId [
=
VariableInitializer]
- VariableDeclaratorId:
- Identifier [Dims]
- VariableDeclaratorId:
- Identifier [Dims]
- _
- Dims:
- {Annotation}
[
]
{{Annotation}[
]
}- VariableInitializer:
- Expression
- ArrayInitializer
ローカル変数宣言は次の場所で使用できます。
ブロック内のローカル変数宣言文(14.4.2)
基本の
for
文のヘッダー(14.14.1)拡張された
for
文のヘッダー(14.14.2)try
-with-resources文のリソース指定(14.20.3)パターン(14.30.1)
ローカル変数宣言の注釈修飾子に関するルールについては、9.7.4および9.7.5に規定されています。
キーワードfinal
がローカル変数宣言の修飾子として表示される場合、ローカル変数はfinal
変数です(4.12.4)。
final
がローカル変数宣言の修飾子として複数回出現する場合は、コンパイル時にエラーが発生します。
Identifierを含んでおらず、イニシャライザを持たないローカル変数宣言が次の場所で使用されている場合、コンパイル時にエラーが発生します。
LocalVariableTypeがvar
で、次のいずれかに当てはまる場合は、コンパイル時にエラーが発生します。
VariableDeclaratorが複数回リストされています。
VariableDeclaratorIdに1つ以上のカッコのペアがあります。
VariableDeclaratorにイニシャライザがありません。
VariableDeclaratorのイニシャライザがArrayInitializerです。
VariableDeclaratorのイニシャライザに変数への参照が含まれています。
例14.4-1.var
で宣言されるローカル変数
次のコードは、var
の使用を制限するこれらのルールを示しています。
var a = 1; // Legal
var b = 2, c = 3.0; // Illegal: multiple declarators
var d[] = new int[4]; // Illegal: extra bracket pairs
var e; // Illegal: no initializer
var f = { 6 }; // Illegal: array initializer
var g = (g = 7); // Illegal: self reference in initializer
これらの制限は、var
で表されている型の混乱を避けるために役立ちます。
14.11 switch
文
switch
文は、式の値に応じて、いくつかの文または式の1つに制御を転送します。
- SwitchStatement:
-
switch
(
Expression)
SwitchBlock
Expressionはセレクタ式と呼ばれます。セレクタ式のタイプはchar
、byte
、short
、int
または参照タイプである必要があり、そうでない場合、コンパイル時にエラーが発生します。
14.11.1 Switchブロック
switch
文およびswitch
式(15.28)の両方の本体は、switchブロックと呼ばれます。このサブセクションでは、switch
文またはswitch
式のどちらに出現するかに関係なく、すべてのswitchブロックに適用される一般的なルールを示します。他のサブセクションでは、switch
文のswitchブロック(14.11.2)またはswitch
式のswitchブロック(15.28.1)のいずれかに適用される追加ルールを示します。
- SwitchBlock:
-
{
SwitchRule {SwitchRule}}
-
{
{SwitchBlockStatementGroup} {SwitchLabel:
}}
- SwitchRule:
-
SwitchLabel
->
Expression;
-
SwitchLabel
->
Block -
SwitchLabel
->
ThrowStatement - SwitchBlockStatementGroup:
-
SwitchLabel
:
{ SwitchLabel:
} BlockStatements
- SwitchLabel:
-
case
CaseConstant {,
CaseConstant} -
case null
[, default
] -
case
CasePattern [ Guard ] -
default
- SwitchLabel:
-
case
CaseConstant {,
CaseConstant} -
case null
[, default
] -
case
CasePattern{,
CasePattern } [ Guard ] -
default
- CaseConstant:
- ConditionalExpression
- CasePattern:
- Pattern
- Guard:
-
when
Expression
switchブロックは次のいずれかで構成できます。
Switchルール。これは、
->
を使用して、switchルール式、switchルール・ブロックまたはswitchルールthrow
文を導入します。またはSwitchラベルが付いた文グループ。これは
:
を使用してswitchラベルが付いたブロック文を導入します。
すべてのswitchルールおよびswitchラベルの付いた文グループは、switchラベル (case
ラベルまたはdefault
ラベルのいずれか)で始まります。複数のswitchラベルが、1つのswitchラベルが付いた文グループに許可されます。
case
ラベルは、case
定数のリストまたは単一の1つ以上のcase
パターンのいずれかで構成されます。
すべてのケース定数を含むケース・ラベルの場合、すべてのcase
定数は、(1)null
リテラル、(2)定数式(15.29)または(3) enum定数の(単純または修飾)名(8.9.1)のいずれかである必要があり、そうでない場合、コンパイル時にエラーが発生します。単一のnull
のcase定数をdefault
キーワードとペアにすることもできます。ケース・パターンのあるケース・ラベルでは、そのケース・パターンのいずれかが1つ以上のパターン変数を宣言している場合、コンパイル時エラーになります。
case
ラベルにパターン変数を宣言するcase
パターンが複数ある場合、適用するcase
ラベルがあると、どの変数が初期化されるかが明確になりません。たとえば、コード・フラグメントswitch (obj) { case Integer i, Boolean b -> { ... } ... }
では、->
の右側のブロックでパターン変数i
とb
のどちらが初期化されるか明確ではないため、コンパイル時エラーになります。フラグメントswitch(obj) { case Integer i, Boolean _ -> { ... } ... }
でも、パターン変数i
が初期化されるかどうかが明確ではないため、コンパイル時エラーになります。フラグメントswitch (obj) { case Integer _, Boolean _ -> { ... } ... }
では、コンパイル時エラーは発生しません。
case
パターンを持つcase
ラベルには、ガードと呼ばれるオプションのwhen
式を含めることができます。これは、パターンに一致する値に対する追加のテストを表します。case
ラベルは、(i)その要素にガードがない場合、または(ii)値がtrue
の定数式(15.29)であるガードがその要素にある場合に、ガードなしと表現されます。それ以外の場合はガード付きです。
switchラベルおよびそのcase
定数およびcase
パターンは、switchブロックに関連付けられていると言い表されます。
特定のswitchブロックの場合、次の両方が当てはまる必要があり、そうでない場合、コンパイル時にエラーが発生します:
- switchブロックに関連付けられている2つの
case
定数が同じ値を持つことがない。
default
ラベルが1つのみ、switchブロックに関連付けられている。
case
ラベルに関連付けられているガードは、boolean
型またはBoolean
型である必要があります。ガードによって宣言されていない変数は、final
または事実上final (4.12.4)であり、(15.26)、増分([15.14.2])、または減分([15.14.3])に割り当てることはできません。そうでない場合、コンパイル時エラーが発生します。ガードがfalse
の値を持つ定数式(15.29)の場合は、コンパイル時エラーになります。
次のすべてに該当する場合、switch
文またはswitch
式のswitchブロックは、セレクタ式T型とのswitch互換性があります:
null
定数がswitchブロックに関連付けられている場合、Tは参照タイプです。Tがenum型の場合は、enum定数の名前であるswitchブロックに関連付けられているすべての
case
定数がT型のenum定数の名前になります。Tがenum型でない場合は、enum定数の名前であるswitchブロックに関連付けられているすべての
case
定数が、Tとの代入の互換性のあるenum定数の修飾名になります(5.2)。定数式であるswitchブロックに関連付けられているすべての
case
定数はTと代入の互換性があり、Tはchar
、byte
、short
、int
、Character
、Byte
、Short
、Integer
またはString
のいずれかです。すべてのパターンpはswitchブロックに関連付けられ、pは型T (14.30.3)に適用できます。
switchブロックは、
boolean
、long
、float
およびdouble
型と連携するように設計されていません。switch
文のセレクタ式またはswitch
式には、これらの型のどれも指定できません。
switch
文またはswitch
式のswitchブロックは、セレクタ式の型とのswitch互換性が必要であり、そうでない場合はコンパイル時にエラーが発生します。
switchブロック内のswitchラベルは、それが適用されるすべての値に対して、前述のいずれかのswitchラベルも適用される場合、それより優先されないものとみなされます。switchブロック内のどのswitchラベルも優先されない場合、コンパイル時にエラーになります。switchラベルが優先されるかどうかを判断するするためのルールは次のとおりです。
switchブロック内に、
case
パターンpを持つガードされていないcase
ラベルが先行し、pがq (14.30.3)より優先される場合、case
パターンqを持つcase
ラベルは優先されません。パターンが別のパターンより優先される定義は型に基づきます。たとえば、型パターンObject oは型パターンString sより優先されるため、その後の結果はコンパイル時エラーになります。
Object obj = ... switch (obj) { case Object o -> System.out.println("An object"); case String s -> // Error - dominated case label System.out.println("A string"); }
case
パターンを持つガード付きcase
ラベルよりも、同じパターンを持つガードのないcase
ラベルが優先されます。たとえば、次はコンパイル時にエラーになります。String str = ...; switch (str) { case String s -> System.out.println("A string"); case String s when s.length() == 2 -> // Error - dominated case label System.out.println("Two character string"); ... }
一方、
case
パターンを持つガード付きcase
ラベルは、同じcase
パターンを持つガード付きでないcase
ラベルより優先されるとみなされません。これにより、次の一般的なパターン・プログラミング・スタイルが可能になります。Integer j = ...; switch (j) { case Integer i when i <= 0 -> System.out.println("Less than or equal to zero"); case Integer i -> System.out.println("An integer"); }
唯一の例外は、ガードが値
true
を含む定数式の場合です。次に例を示します。Integer j = ...; switch (j) { case Integer i when true -> // Allowed but why write this? System.out.println("An integer"); case Integer i -> // Error - dominated case label System.out.println("An integer"); }
複数パターンを持つケース・ラベルは、そのパターンのいずれかが先行するガード付きでないケース・ラベルでケース・パターンとして出現するパターンより優先されない場合、これらのパターンのケース・ラベルも優先されません。たとえば、先行するガード付きでないケースの
Number _
のほうが、Integer _
より優先されます。Object o = ... switch (o) { case Number _ -> System.out.println("A Number"); case Integer _, String _ -> // Error - dominated case pattern: `Integer _` System.out.println("An Integer or a String"); }
次のいずれかが保持されている場合、
case
定数cを持つcase
ラベルは優先されません。cはプリミティブ型Sの定数式であり、switchブロックには先行する
case
ラベルがあり、case
パターンpがあります。ここで、pはSのラッパー・クラスでは制限されていません。cは参照型Tの定数式であり、switchブロックには先行する
case
ラベルがあり、case
パターンpがあります。ここで、pは型Tでは制限されていません。cは型Tのenum式であり、switchブロックには先行する
case
ラベルがあり、case
パターンpがあります。ここで、pは型Tでは制限されていません。
たとえば、
Integer
型パターンを持つcase
ラベルは、整数リテラルを持つcase
ラベルより優先されます。Integer j = ...; switch (j) { case Integer i -> System.out.println("An integer"); case 42 -> // Error - dominated! System.out.println("42!"); }
一般的に、決定不能なガードの分析は試行されません。たとえば、セレクタ式の値が
42
の場合、最初のswitchラベルが一致しない場合でも、次の結果はコンパイル時エラーになります。Integer j = ...; switch (j) { case Integer i when i != 42 -> System.out.println("An integer that isn't 42"); case 42 -> // Error - dominated! System.out.println("42!"); }
case
定数を持つすべてのcase
ラベルは、case
パターンを持つラベルの前に出現します。次に例を示します:Integer j = ...; switch (j) { case 42 -> System.out.println("42"); case Integer i when i < 50 -> System.out.println("An integer less than 50"); case Integer i -> System.out.println("An integer"); }
switchブロック内に
default
ラベルが先行する場合、case
パターンを持つcase
ラベルは優先されません。switchブロック内に
default
ラベルが先に存在する場合、null
ケース定数を持つcase
ラベルは優先されません。使用する場合、
default
ラベルはswitch
ブロックの最後に配置されます。過去の理由から、
default
ラベルは、null
case
定数またはcase
パターンを持たないcase
ラベルの前に出現します。int i = ...; switch(i) { default -> System.out.println("Some other integer"); case 42 -> // allowed System.out.println("42"); }
このスタイルは新しいコードではお薦めしません。
switchブロック内に先行する
case null, default
ラベルがある場合、switchラベルは優先されません。使用する場合、
case null, default
ラベルは常にswitch
ブロックの最後に配置されます。
case
パターンp (pがセレクタ式の型(14.30.3)に対して無条件)で、switchブロック内に先行するガード付きでないcase
ラベルがある場合、default
ラベルは優先されません。case
パターンp (pがセレクタ式の型(14.30.3)に対して無条件)で、switchブロック内に先行するガード付きでないcase
ラベルがある場合、case null, default
ラベルは優先されません。セレクタ式の型で無条件である
case
パターンを持つcase
ラベルは、名前が示すように、すべての値に一致するため、default
ラベルのように動作します。switchブロックは、default
のように動作する複数のswitchラベルを持つことはできません。
switchブロック内にcase
パターンp1,...,pn (n > 1)があり、パターンの1つpi (1 ≤ i < n)が他のパターンpj (i < j ≤ n)より優先されるcase
パターンがある場合、コンパイル時にエラーが発生します。
switchラベルが付いた文グループで構成されるswitchブロックで、1つ以上のパターン変数を宣言するcase
パターンで文がラベル付けされていて、次のいずれかの場合、コンパイル時にエラーが発生します:
switchブロックの前述の文が正常に完了する可能性がある(14.22)、または
文に複数のswitchラベルが付けられている。
最初の条件により、文グループがパターン変数を初期化せずに別の文グループにフォール・スルーすることがなくなります。たとえば、前述の文グループから到達可能な
case Integer i
というラベルが付けられた文では、パターン変数i
は初期化されていません:Object o = "Hello"; switch (o) { case String s: System.out.println("String: " + s ); // No break! case Integer i: System.out.println(i + 1); // Error! Can be reached // without matching the // pattern `Integer i` default: }
switchラベル文グループで構成されるswitchブロックでは、複数のラベルを文グループに適用できます。2番目の条件は、別のラベルのパターン変数を初期化せずに、あるラベルに基づいて文グループが実行されないようにします。たとえば、次のようになります。
Object o = "Hello World"; switch (o) { case String s: case Integer i: System.out.println(i + 1); // Error! Can be reached // without matching the // pattern `Integer i` default: } Object obj = null; switch (obj) { case null: case String s: System.out.println(s); // Error! Can be reached // without matching the // pattern `String s` default: }
これらの条件は両方とも、
case
パターンがパターン変数を宣言している場合にのみ適用されます。一方、次の例は問題ありません:record R() {} record S() {} Object o = "Hello World"; switch (o) { case String s: System.out.println(s); // No break! case R(): System.out.println("It's either an R or a string"); break; default: } Object ob = new R(); switch (ob) { case R(): case S(): // Multiple case labels! System.out.println("Either R or an S"); break; default: } Object obj = null; switch (obj) { case null: case R(): // Multiple case labels! System.out.println("Either null or an R"); break; default: }
14.11.1.2 実行時に適用されるswitchラベルの決定
switch
文の実行(14.11.3)およびswitch
式の評価(15.28.2)の両方で、switchブロックに関連付けられたswitchラベルがセレクタ式の値に適用されるかどうかを判断する必要があります。これは次のように処理されます。
値がnull参照の場合、
null
のcase
定数を含むcase
ラベルが適用されます。値がnull参照ではない場合、値に適用されるswitchブロックの最初の(ある場合)
case
ラベルを次のように決定します:null以外の
case
定数cを持つcase
ラベルは、最初に値がボックス化解除変換(5.1.8)の対象になり、定数cがボックス化解除した値と等しくなる場合、Character
、Byte
、Short
またはInteger
型の値に適用されます。ボックス化解除変換は、ボックス化解除される値がNULL参照にならないことが保証されるため、正常に完了する必要があります。
等価性は、
==
演算子で定義されます(15.21)。null以外の
case
定数cを持つcase
ラベルは、定数cが値と等しい場合、Character
、Byte
、Short
またはInteger
型ではない値に適用されます。等価性は
==
演算子によって定義されますが、値がString
の場合はString
クラスのequals
メソッドによって定義されます。case
パターンpを持つcase
ラベルが値に適用されることを確認するには、最初に、値がパターンp (14.30.2)に一致するかどうかをチェックします。case
パターンp1,...,pn (n ≥ 1)を持つcase
ラベルが値に適用されることを確認するには、適用される最初のcase
パターンpi (1≤i≤n) (存在する場合)を決定します。適用する
case
パターンを決定するプロセスが突然完了した場合は、適用されるswitchラベルを判断するプロセスについても同じ理由で突然完了します。case
パターンpが値に適用されることを確認するには、最初に、値がパターンp (14.30.2)に一致するかどうかをチェックします。パターン・マッチングが突然完了した場合は、適用される
switchラベルcase
パターンを判断するプロセスについても同じ理由で突然完了します。パターン・マッチングが成功し、
case
ラベルパターンがガードされていない場合、このcase
ラベルパターンが適用されます。パターン・マッチングが成功し、
case
ラベルパターンがガードされている場合、ガードが評価されます。その結果がBoolean
型の場合は、ボックス化解除変換(5.1.8)の対象になります。ガードまたは後続のボックス化解除変換(ある場合)の評価がなんらかの理由で突然完了した場合、適用する
switchラベルパターンを判断するプロセスは同じ理由で突然完了します。それ以外の場合は、その結果の値が
true
のときに、case
ラベルパターンが適用されます。case null, default
ラベルはすべての値に適用されます
値がnull参照ではなく、ステップ2のルールに従って
case
ラベルが適用されない場合、default
ラベルがswitchブロックに関連付けられている場合は、そのラベルが適用されます。
単一の
case
ラベルには、複数のcase
定数を含めることができます。ラベルは、その定数のいずれかがセレクタ式の値と等しい場合、セレクタ式の値に適用されます。たとえば、次のコードで、enum変数day
が表示されているenum定数のいずれかである場合、case
ラベルは一致します:switch (day) { ... case SATURDAY, SUNDAY : System.out.println("It's the weekend!"); break; ... }
case
パターンのあるcase
ラベルが適用される場合、これは、パターンに対する値のパターン・マッチング・プロセスが成功したことによるものです(14.30.2)。値がパターンと正常に一致すると、パターン・マッチングのプロセスは、パターンによって宣言されたパターン変数を初期化します。
過去の理由から、
default
ラベルは、default
ラベルの後に一部のラベルが出現する場合でも、すべてのcase
ラベルが一致しなかった場合にのみ考慮されます。ただし、後続のラベルでは、null
以外のcase定数(14.11.1)のみを使用でき、スタイルによっては、プログラマはdefault
ラベルを最後に配置することをお薦めします。
CおよびC++で、
switch
文の本体は文(単数および複数)にすることができ、case
ラベルをその文で直接含む必要はありません。単純なループを考えてみましょう。for (i = 0; i < n; ++i) foo();
ここで
n
は正であると知られています。ダフスデバイスとして知られているトリックは、ループをアンロールするためにCまたはC++で使用できますが、これはJavaプログラミング言語では有効なコードではありません。int q = (n+7)/8; switch (n%8) { case 0: do { foo(); // Great C hack, Tom, case 7: foo(); // but it's not valid here. case 6: foo(); case 5: foo(); case 4: foo(); case 3: foo(); case 2: foo(); case 1: foo(); } while (--q > 0); }
幸いにも、このトリックは広く認識または使用されてはいないようです。さらに、今日ではあまり必要ではなくなりました。この種のコード変換は、適切に最新式の最適化コンパイラの領域にあります。
14.14 for
文
14.14.2 拡張されたfor
文
拡張されたfor
文の形式は次のようになります。
- EnhancedForStatement:
-
for
(
LocalVariableDeclaration:
Expression)
Statement - EnhancedForStatementNoShortIf:
-
for
(
LocalVariableDeclaration:
Expression)
StatementNoShortIf
- LocalVariableDeclaration:
- {VariableModifier} LocalVariableType VariableDeclaratorList
- VariableModifier:
- Annotation
final
- LocalVariableType:
- UnannType
var
- VariableDeclaratorList:
- VariableDeclarator {
,
VariableDeclarator}- VariableDeclarator:
- VariableDeclaratorId [
=
VariableInitializer]
- VariableDeclaratorId:
- Identifier [Dims]
- VariableDeclaratorId:
- Identifier [Dims]
- _
- Dims:
- {Annotation}
[
]
{{Annotation}[
]
}
式の型は、配列型(10.1)またはRAW型Iterable
のサブタイプである必要があります。そうしないと、コンパイル時にエラーが発生します。
拡張されたfor
文のヘッダーは、VariableDeclaratorIdが指定した識別子であるローカル変数を宣言するか、あるいは名前のないローカル変数(6.1)を宣言します。拡張されたfor
文が実行されると、ローカル変数がそれぞれのループの反復で、式によって生成された連続するIterable
の要素または配列に初期化されます。
拡張されたfor
文のヘッダーで宣言されたローカル変数のルールは、14.4で規定され、LocalVariableTypeがvar
の場合に適用されるそのセクション内のルールが無視されます。さらに、次のすべてが当てはまる必要があり、そうでない場合、コンパイル時にエラーが発生します。
VariableDeclaratorListは単一のVariableDeclaratorで構成されています。
VariableDeclaratorにはイニシャライザがありません。
LocalVariableTypeが
var
の場合、VariableDeclaratorIdにはカッコのペアがありません。
拡張されたfor
文のヘッダーで宣言されているローカル変数のスコープとシャドウ化は、6.3および6.4で規定されています。
ネストされたクラスまたはインタフェースまたはラムダ式からのローカル変数への参照は、6.5.6.1で規定されているように制限されます。
拡張されたfor
文のヘッダーで宣言されたローカル変数の型Tは、次のように決定されます:
LocalVariableTypeがUnannTypeで、UnannTypeまたはVariableDeclaratorIdにカッコのペアが出現しない場合、TはUnannTypeで示される型です。
LocalVariableTypeがUnannTypeで、カッコのペアがUnannTypeまたはVariableDeclaratorIdに出現する場合、Tは10.2によって規定されています。
LocalVariableTypeが
var
の場合、Rは次のようにExpressionの型から導出されます:Expressionに配列型がある場合、Rは配列型のコンポーネント型です。
そうでない場合、Expressionに一部の型Xについて
Iterable<
X>
のサブタイプである型がある場合、RはXになります。そうでない場合、ExpressionにはRAW型
Iterable
のサブタイプである型があり、RはObject
です。
Tは、R (4.10.5)で記述されているすべての合成型変数に関するRの上方投影です。
拡張されたfor
文ヘッダーでローカル変数識別子を宣言することの正確な意味は、次のように基本for
文への変換によって規定されます。
式の型が
Iterable
のサブタイプである場合、基本のfor
文の形式は次のとおりです:for (I #i = Expression.iterator(); #i.hasNext(); ) { {VariableModifier} T Identifier = (TargetType) #i.next(); Statement }
ここで:
式の型が
Iterable<
X>
のサブタイプで、一部の型引数Xの場合、Iはjava.util.Iterator<
X>
型です。それ以外の場合、IはRAW型java.util.Iterator
です。#i
は自動生成される識別子となり、拡張されたfor
文が出現するポイントでスコープ(6.3)内にある、他のいかなる識別子(自動生成またはその他)とも異なります。{VariableModifier}は、拡張された
for
文のヘッダーで指定されています。Tは、前述のローカル変数の型です。
Tが参照型の場合、TargetTypeはTです。それ以外の場合、TargetTypeはIの型引数の取得変換(5.1.10)の上限、またはIがRAWの場合は
Object
です。
それ以外の場合、式には必ず配列型S
[]
があり、基本のfor
文の形式は次のとおりです:S[] #a = Expression; L1: L2: ... Lm: for (int #i = 0; #i < #a.length; #i++) { {VariableModifier} T Identifier = #a[#i]; Statement }
ここで:
L1 ... Lmは、拡張された
for
文の直前の(空の場合もある)ラベルのシーケンスです。#a
および#i
は自動生成される識別子となり、拡張されたfor
文が出現するポイントでスコープ内にある、他のいかなる識別子(自動生成またはその他)とも異なります。{VariableModifier}は、拡張された
for
文のヘッダーで指定されています。Tは、前述のローカル変数の型です。
たとえば、次のコードがあるとします。
List<? extends Integer> l = ... for (float i : l) ...
次のように変換されます。
for (Iterator<Integer> #i = l.iterator(); #i.hasNext(); ) { float #i0 = (Integer)#i.next(); ...
例14.14-1.拡張されたfor
および配列
整数配列の合計を計算する次のプログラムは、拡張されたfor
が配列に対してどのように機能するかを示しています。
int sum(int[] a) {
int sum = 0;
for (int i : a) sum += i;
return sum;
}
例14.14-2.拡張されたfor
およびボックス化解除変換
次のプログラムは、拡張されたfor
文と自動ボックス化解除を組み合せて、ヒストグラムを度数分布表に変換します。
Map<String, Integer> histogram = ...;
double total = 0;
for (int i : histogram.values())
total += i;
for (Map.Entry<String, Integer> e : histogram.entrySet())
System.out.println(e.getKey() + " " + e.getValue() / total);
}
拡張されたfor
文のヘッダーで名前のないローカル変数を宣言することの厳密な意味は、前述のように基本のfor
文に変換し、名前のないローカル変数を識別子のかわりに使用することで規定できます。
14.20 try
文
14.20.3 try
-with-resources
try
-with-resources文は、try
ブロックの実行後に初期化された順序とは逆に、変数(resourcesと呼ばれます)でパラメータ化され、try
ブロックの実行前に初期化され、自動的に終了します。catch
句とfinally
句は通常、リソースが自動的に終了すると不要になります。
- TryWithResourcesStatement:
-
try
ResourceSpecification Block [Catches] [Finally] - ResourceSpecification:
-
(
ResourceList [;
])
- ResourceList:
-
Resource {
;
Resource} - リソース:
- LocalVariableDeclaration
- VariableAccess
- VariableAccess:
- ExpressionName
- FieldAccess
- LocalVariableDeclaration:
- {VariableModifier} LocalVariableType VariableDeclaratorList
- VariableModifier:
- Annotation
final
- LocalVariableType:
- UnannType
var
- VariableDeclaratorList:
- VariableDeclarator {
,
VariableDeclarator}- VariableDeclarator:
- VariableDeclaratorId [
=
VariableInitializer]
- VariableDeclaratorId:
- Identifier [Dims]
- VariableDeclaratorId:
- Identifier [Dims]
- _
- Dims:
- {Annotation}
[
]
{{Annotation}[
]
}- VariableInitializer:
- Expression
- ArrayInitializer
UnannTypeについては、8.3を参照してください。
リソースの指定は、イニシャライザ式でローカル変数を宣言、または既存の変数を参照することによって、try
-with-resources文のresourcesを示します。既存の変数は式名(6.5.6)またはフィールド・アクセス式(15.11)によって参照されます。
リソース指定で宣言されたローカル変数のルールは、14.4で規定されています。さらに、次のすべてが当てはまる必要があり、そうでない場合、コンパイル時にエラーが発生します。
VariableDeclaratorListは単一のVariableDeclaratorで構成されています。
VariableDeclaratorにイニシャライザがあります。
VariableDeclaratorIdにカッコのペアがありません。
リソース指定で宣言されたローカル変数のスコープとシャドウ化は、6.3および6.4で規定されています。
ネストされたクラスまたはインタフェースまたはラムダ式からのローカル変数への参照は、6.5.6.1で規定されているように制限されます。
リソース指定で宣言されたローカル変数の型は、14.4.1で規定されています。
リソース指定で宣言されたローカル変数の型、またはリソース指定で参照された既存の変数の型はAutoCloseable
のサブタイプである必要があり、そうでない場合はコンパイル時にエラーが発生します。
リソースの指定で、同じ名前のローカル変数を複数宣言すると、コンパイル時にエラーが発生します。
リソース指定には、複数の名前のないローカル変数(6.1)の宣言が含まれている場合があります。
リソースは、次の点でfinal
です。
リソース指定で宣言されるローカル変数は、明示的に
final
として宣言されない場合は暗黙的にfinal
として宣言されます(4.12.4)。リソース指定で参照される既存の変数は、
final
、またはtry
-with-resources文の前に明確に割り当てられた実質的にfinal
変数である必要があり、(16)そうでない場合はコンパイル時にエラーが発生します。
リソースは左から右に初期化されます。リソースの初期化に失敗(つまり、イニシャライザ式が例外をスロー)する場合、それまでtry
-with-resources文で初期化されたリソースはすべてクローズされます。すべてのリソースの初期化が完了すると、try
ブロックが正常に実行され、その後、try
-with-resources文のnullでないリソースがクローズされます。
リソースは、初期化された位置から逆方向にクローズします。リソースは、nullではない値に初期化された場合のみクローズします。1つのリソースのクローズからの例外が他のリソースのクローズを妨げることはありません。イニシャライザ、try
ブロック、またはリソースのクローズによってすでに例外がスローされている場合、例外は非表示になります。
リソース指定が複数のリソースを示すtry
-with-resources文は、複数のtry
-with-resources文であり、そのそれぞれが単一のリソースを示すリソース指定を持つものとして扱われます。nリソース(n > 1)を持つtry
-with-resources文が変換されると、その結果はn-1リソースを持つtry
-with-resources文になります。nが変換されると、nがtry
-catch
-finally
文にネストされ、全体の変換が完了します。
14.20.3.1基本のtry
-with-resources
catch
句またはfinally
句のないtry
-with-resources文は、基本のtry
-with-resources文と呼ばれます。
基本のtry
-with-resources文の形式が次の場合:
try (VariableAccess ...)
Block
リソースは、次の変換によって最初にローカル変数宣言に変換されます。
try (T #r = VariableAccess ...) {
Block
}
T
はVariableAccessが示す変数の型、#r
は自動生成される識別子であり、try
-with-resources文が出現するポイントでスコープ内にある、他のいかなる識別子(自動生成またはその他)とも異なります。try
-with-resources文は、このセクションの後半部分に従って変換されます。
次の形式の基本のtry
-with-resources文の意味:
try ({VariableModifier} R Identifier = Expression ...)
Block
これはローカル変数宣言およびtry
-catch
-finally
文への次のような変換によって指定されます。
{
final {VariableModifierNoFinal} R Identifier = Expression;
Throwable #primaryExc = null;
try ResourceSpecification_tail
Block
catch (Throwable #t) {
#primaryExc = #t;
throw #t;
} finally {
if (Identifier != null) {
if (#primaryExc != null) {
try {
Identifier.close();
} catch (Throwable #suppressedExc) {
#primaryExc.addSuppressed(#suppressedExc);
}
} else {
Identifier.close();
}
}
}
}
{VariableModifierNoFinal}はfinal
(存在する場合)なしで{VariableModifier}として定義されます。
#t
、#primaryExc
、および#suppressedExc
は自動生成される識別子となり、try
-with-resources文が出現するポイントでスコープ内にある、他のいかなる識別子(自動生成またはその他)とも異なります。
または、次の形式の基本のtry
-with-resources文の意味:
try ({VariableModifier} R _ = Expression ...)
Block
これはローカル変数宣言およびtry
-catch
-finally
文への次のような変換によって指定されます。
{
final {VariableModifierNoFinal} R #i = Expression;
Throwable #primaryExc = null;
try ResourceSpecification_tail
Block
catch (Throwable #t) {
#primaryExc = #t;
throw #t;
} finally {
if (#i != null) {
if (#primaryExc != null) {
try {
#i.close();
} catch (Throwable #suppressedExc) {
#primaryExc.addSuppressed(#suppressedExc);
}
} else {
#i.close();
}
}
}
}
{VariableModifierNoFinal}はfinal
(存在する場合)なしで{VariableModifier}として定義されます。
#t
、#primaryExc
、#suppressedExc
、および#i
は自動生成される識別子となり、try
-with-resources文が出現するポイントでスコープ内にある、他のいかなる識別子(自動生成またはその他)とも異なります。
リソース指定が1つのリソースを示している場合、ResourceSpecification_tailは空です(そしてtry
-catch
-finally
文はそれ自体がtry
-with-resources文ではありません)。
リソース指定がn > 1リソースを示している場合、ResourceSpecification_tailは、リソース指定に示されている2番目、3番目...、n番目のリソースと同じ順序で構成されます(そしてtry
-catch
-finally
文はそれ自体がtry
-with-resources文です)。
基本のtry
-with-resources文の到達可能性と明確な代入ルールは、前述の変換変換によって暗黙的に指定されます。
単一のリソースを管理する基本のtry
-with-resources文では、次のようになります。
値Vの
throw
のためにリソースの初期化が突然完了した場合、try
-with-resources文は、値Vのthrow
のために突然完了します。リソースの初期化が正常に完了し、
try
ブロックが値Vのthrow
のために突然完了した場合は、次のようになります。リソースの自動クローズが正常に完了した場合、
try
-with-resources文は値Vのthrow
のために突然完了します。値V2の
throw
のためにリソースの自動クローズが突然完了した場合、try
-with-resources文は、値Vのthrow
のために突然完了し、V2はVの抑制された例外リストに追加されます。
リソースの初期化が正常に終了し、
try
ブロックも正常に完了し、値Vのthrow
のためにリソースの自動クローズが突然完了した場合、try
-with-resources文は、値Vのthrow
のために突然完了します。
複数のリソースを管理する基本のtry
-with-resources文では、次のようになります。
値Vの
throw
のためにリソースの初期化が突然完了した場合、次のようになります。すべての初期化が完了しているリソース(ゼロの場合もあります)の自動クローズが正常に完了した場合、
try
-with-resources文は値Vのthrow
のために突然完了します。値V1...Vnの
throw
のために、すべての初期化が完了しているリソース(ゼロの場合もあります)の自動クローズが突然完了した場合、try
-with-resources文は、値Vのthrow
のために突然完了し、残りのV1...VnはVの抑制された例外リストに追加されます。
すべてのリソースの初期化が正常に完了し、
try
ブロックが値Vのthrow
のために突然完了した場合は、次のようになります。初期化されたすべてのリソースの自動クローズが正常に完了した場合、
try
-with-resources文は値Vのthrow
のために突然完了します。値V1...Vnの
throw
のために、1つ以上の初期化が完了したリソースの自動クローズが突然完了した場合、try
-with-resources文は、値Vのthrow
のために突然完了し、残りのV1...VnはVの抑制された例外リストに追加されます。
すべてのリソースの初期化が正常に完了し、
try
ブロックが正常に完了した場合は、次のようになります。値Vの
throw
のために初期化されたリソースの1つの自動クローズが突然完了し、初期化されたリソースの他のすべての自動クローズが正常に完了した場合、try
-with-resources文は、値Vのthrow
のために突然完了します。値がV1...Vn(ここで、V1は右端のリソースがクローズに失敗する例外で、Vnは左端のリソースがクローズに失敗する例外です)の
throw
のため、初期化されたリソースの複数の自動クローズが突然完了した場合、try
-with-resources文は、値V1のthrow
のために突然完了し、残りの値V2...VnはV1の抑制された例外リストに追加されます。
14.20.3.2 拡張try
-with-resources
少なくとも1つのcatch
句またはfinally
句(あるいはその両方)を含むtry
-with-resources文は、拡張try
-with-resources文と呼ばれます。
拡張try
-with-resources文の意味:
try ResourceSpecification
Block
[Catches]
[Finally]
これはtry
-catch
文、try
-finally
文、またはtry
-catch
-finally
文内にネストされた基本のtry
-with-resources文への次の変換によって指定されます。
try {
try ResourceSpecification
Block
}
[Catches]
[Finally]
変換すると、リソース指定がtry
文内に配置されます。これにより、拡張try
-with-resources文のcatch
句で、リソースの自動初期化またはクローズによる例外を捕捉できます。
さらに、finally
キーワードの意図に従って、finally
ブロックが実行されるまでにすべてのリソースがクローズ(またはクローズが試行)されます。
14.30 パターン
14.30.1 パターンの種類
次の変更は、JEP 440 (レコード・パターン)およびJEP 441 (switch
のパターン・マッチング)によるJLS変更が適用されていることを前提としています(JLS:JEP440+441)。
型パターンは、値がパターンに出現する型のインスタンスであるかどうかをテストするために使用されます。レコード・パターンは、値がレコード・クラス型のインスタンスであるかどうかをテストし、そうである場合は、レコード・コンポーネント値に対してパターン・マッチングを再帰的に実行するために使用されます。
- Pattern:
- TypePattern
- RecordPattern
- TypePattern:
- LocalVariableDeclaration
- RecordPattern:
-
ReferenceType
(
[PatternListComponentPatternList ])
- PatternList :
-
Pattern {
,
Pattern }
- ComponentPatternList:
-
ComponentPattern {
,
ComponentPattern } - ComponentPattern:
- Pattern
- UnnamedPattern
- UnnamedPattern:
- _
- LocalVariableDeclaration:
- {VariableModifier} LocalVariableType VariableDeclaratorList
- VariableModifier:
- Annotation
final
- LocalVariableType:
- UnannType
var
- VariableDeclaratorList:
- VariableDeclarator {
,
VariableDeclarator}- VariableDeclarator:
- VariableDeclaratorId [
=
VariableInitializer]
- VariableDeclaratorId:
- Identifier [Dims]
- VariableDeclaratorId:
- Identifier [Dims]
- _
- Dims:
- {Annotation}
[
]
{{Annotation}[
]
}
UnannTypeについては、8.3を参照してください。
レコード・パターンのネストされたパターン・リストに要素として出現しないパターンは、トップレベル型パターンと呼ばれ、それ以外はネストしたパターンと呼ばれます。
型パターンは1つのパターン変数を宣言します。このパターン変数は、(_
で示される)名前のないパターン変数にできます。それ以外の場合は、ローカル変数宣言の識別子でパターン変数の名前が指定されます。
型パターンで宣言されたパターン変数のルールについては、14.4に規定されています。さらに、次のすべてが当てはまる必要があり、そうでない場合、コンパイル時にエラーが発生します。
トップレベル型パターンのLocalVariableTypeは、参照型(また、
var
でないこと)を示します。VariableDeclaratorListは単一のVariableDeclaratorで構成されています。
VariableDeclaratorにはイニシャライザがありません。
VariableDeclaratorIdにカッコのペアがありません。
トップレベル型パターンで宣言されたパターン変数の型は、LocalVariableTypeによって示される参照型です。
ネストされた型パターンで宣言されたパターン変数の型は、次のように決定されます。
LocalVariableTypeがUnannTypeの場合、パターン変数の型はUnannTypeで示されます
LocalVariableTypeが
var
の場合、型パターンは、型Rのレコード・パターンのコンポーネント・パターン・リストにネストされている必要があります。TをRの対応するコンポーネント・フィールドの型にします。パターン変数の型はTで指定しているすべての合成型変数に関するTの上方投影です。次のレコード宣言について考えてみます。
record R<T>(ArrayList<T> r){}
パターン
R<String>(var r)
の場合、パターン変数r
の型はArrayList<String>
です。
型パターンは、型がRのレコード・パターンのパターン・リストに要素として表示され、Rの対応するレコード・コンポーネントに型Uがあり、型パターンが型U (14.30.3)に対して無条件である場合、null一致と呼ばれます。
型パターンのこのコンパイル時プロパティは、パターン・マッチング(14.30.2)のプロセスで使用されるため、実行時に使用する型パターンに関連付けられています。
レコード・パターンは、ReferenceTypeとネストされたコンポーネント・パターン・リストで構成されます。ReferenceTypeがレコード・クラス・タイプ(8.10)でない場合、コンパイル時エラーが発生します。
ReferenceTypeがRAW型である場合、[18.5.5]で説明されているように、レコード・パターンの型が推論されます。レコード・パターンに対して型を推論できない場合、コンパイル時にエラーが発生します。
それ以外の場合、レコード・パターンの型はReferenceTypeです。
レコード・パターンのネストされたコンポーネント・パターン・リストの長さは、ReferenceTypeで指定されたレコード・クラスの宣言内のレコード・コンポーネント・リストと同じ長さである必要があります。そうでない場合は、コンパイル時にエラーが発生します。
現時点では、可変個引数レコード・パターンはサポートされていません。これは、Javaプログラミング言語の将来のバージョンでサポートされる可能性があります。
レコード・パターンでは、ネストされたコンポーネント・パターン・リスト内のパターンで宣言されているパターン変数(ある場合)を宣言します。
名前のないパターンは、型がRのレコード・パターンのコンポーネント・パターン・リストに要素としてのみ表示できる特殊なパターンです。TをRの対応するコンポーネント・フィールドの型にします。名前のないパターンはパターン変数を宣言せず、その型はTが示すすべての合成型変数に関してTの上方投影として定義されます。名前のないパターンは常にnull一致です。
14.30.2 パターン・マッチング
パターン・マッチングは、実行時にパターンに対して値をテストするプロセスです。パターン・マッチングは、文実行(14.1)および式評価(15.1)とは異なります。値がパターンに正常に一致する場合、パターン・マッチング・プロセスが、パターンによって宣言されたすべてのパターン変数(ある場合)を初期化します。
パターン・マッチングのプロセスには、式の評価または文の実行が関与する場合があります。したがって、式の評価または文の実行が突然完了する場合は、パターン・マッチングが突然完了すると表現されます。突然の完了には常に理由が関連付けられており、それは常に、指定された値を持つthrow
です。パターン・マッチングは、突然完了しない場合、正常に完了すると言い表されます。
値がパターンと一致するかどうかを決定するルールおよびパターン変数を初期化するルールは、次のとおりです。
null参照は
型パターンがnull一致の場合(14.30.1)、型パターンnull一致パターンと一致し、そうでない場合は一致しません。null参照が一致する場合、
型null一致パターンによって宣言されたパターン変数(存在する場合)がnull参照に初期化されます。null参照が一致しない場合、型
null一致パターンによって宣言されたパターン変数(存在する場合)は初期化されません。null参照ではない値vは、
ClassCastException
を発生させずにvをTにキャストできる場合、型Tの型パターンと一致します。そうでない場合、一致しません。vが一致する場合、型パターンによって宣言されたパターン変数はvに初期化されます。
vが一致しない場合、型パターンによって宣言されたパターン変数は初期化されません。
null参照は、レコード・パターンと一致しません。
この場合、レコード・パターンによって宣言されたanyパターンの変数は初期化されません。
null参照ではない値vは、(i)
ClassCastException
の発生なしにvをRにキャストでき、(ii) vの各レコード・コンポーネントがLの対応するパターンと一致する場合、型Rのレコード・パターンおよびネストされたコンポーネント・パターン・リストLと一致します。それ以外の場合は一致しません。vの各レコード・コンポーネントは、そのコンポーネントに対応するvのアクセサ・メソッドを呼び出すことによって決定されます。アクセサ・メソッドの呼出しの実行が理由Sのために突然完了した場合、パターン・マッチングは原因Sによる
MatchException
をスローすることで突然完了します。ネストされたコンポーネント・パターン・リストに現れるパターンで宣言されたパターン変数は、リスト内のすべてのパターンが一致する場合にのみ初期化されます。