様々なコンパイル時分析には、まだ知られていない型についての推論が必要です。 その中の主体は、汎用メソッド適用性テスト(§18.5.1)と汎用メソッド呼出し型推論(§18.5.2)です。 一般に、未知の型について推論するプロセスを型推論と呼びます。
大まかに言うと、型推論は次の3つのプロセスに分解できます。
削減は、制約式と呼ばれる式またはタイプに関する互換性アサーションを取得し、推論変数に対する一連の境界に縮小します。 多くの場合、制約式は他の制約式に縮小され、再帰的に縮小する必要があります。 プロシージャに従って、これらの追加の制約式を識別し、最終的にバインドされたセットを介して表現するには、推測型の選択によって各制約式がtrueになる条件を設定します。
Incorporationでは、一連の推論変数境界が保持され、新しい境界が追加されるたびにこれらが一貫していることが保証されます。 ある変数の境界は、別の変数の選択肢に影響を与えることがあるため、このプロセスは、このような相互依存変数間に境界を伝播します。
解決では、推論変数の境界が調べられ、それらの境界と互換性のあるインスタンス化が決定されます。 また、相互依存推論変数を解決する順序も決定します。
これらのプロセスは密接に相互作用します。削減は結合をトリガーし、結合はさらなる削減につながる可能性があり、解決はさらなる結合を引き起こす可能性があります。
The Java®Language SpecificationのJava SE 7 Editionと比較して、推論に対する重要な変更は次のとおりです。
メソッド呼出し引数としてラムダ式およびメソッド参照のサポートを追加しています。
ポリ式による推論を定義するための一般化。推論が完了する後まで型が明確に定義されていない場合があります。 これにより、ネストされた汎用メソッドおよびダイヤモンド・コンストラクタ呼出しの推論が改善されるという顕著な効果があります。
ワイルドカード・パラメータ化された機能インタフェース・ターゲット・タイプおよび最も具体的なメソッド分析の処理に推論がどのように使用されるかの説明。
呼出し適用性テスト(呼出し引数のみを含む)と呼出しタイプ推論(ターゲット・タイプを組み込む)の違いの明確化。
より良い結果を得るために、呼び出し型推論まで、すべての推論変数の分解を遅らせます。
相互依存(または自己依存)変数の推論動作を改善します。
バグや潜在的な混乱の原因を排除します。 このリビジョンは、特定の変換コンテキストとサブタイピングの区別をより慎重かつ正確に処理し、対応する非推論関係と並行して縮小を記述します。 非推論関係からの意図的な出発がある場合、これらは明示的にそのように識別されます。
将来の進化の基盤を築く: 推論の拡張または新しいアプリケーションは、仕様への統合が容易になります。
この項では、用語がこの章全体で使用されるため、「推測変数」、「制約式」および「境界」を定義します。 表記も示しています。
推論変数は、型のメタ変数です。つまり、型に関する抽象的な推論を可能にする特別な名前です。 型変数と区別するために、推論変数はギリシャ文字(主にα)で表されます。
この章の「型」という用語は、推論変数を含む型のような構文を含めるために大まかに使用されます。 適切な型という用語は、推論変数を記述するそのような型を除外します。 推論変数を含むアサーションは、各推論変数を適切な型に置き換えることで生成できる、すべての適切な型に関するアサーションです。
制約式は、推論変数を含む可能性のある互換性またはサブタイピングのアサーションです。 式には、次のいずれかの形式を使用できます。
'Expression → T': 式は、T型(§5.3)の緩い呼出しコンテキストで互換性があります。
'S → T': 型Sは、型T (§5.3)の緩い呼出しコンテキストで互換性があります。
'S <: T': 参照型Sは、参照型T (§4.10)のサブタイプです。
'S <= T': 型引数Sは、型引数T (§4.5.1)によって含められます。
'S = T': 型Sは型T (§4.3.4)と同じであるか、型引数Sは型引数Tと同じです。
'LambdaExpression →throws T': LambdaExpressionの本体によってスローされたチェック済例外は、Tから導出されたファンクション・タイプのthrows句によって宣言されます。
'MethodReference →throws T': 参照されたメソッドによってスローされるチェックされた例外は、Tから導出されたファンクション・タイプのthrows句によって宣言されます。
制約式の例:
Collections.singleton("hi")には、制約式'"hi" → α'があります。 削減によって、これは制約式'String <: α'になります。
Arrays.asList(1, 2.0)には、制約式'1 → α'および'2.0 → α'があります。 削減によって、これらは制約式'int → α'および'double → α'となり、'Integer <: α'および'Double <: α'になります。
コンストラクタ起動List<Thread> lt = new ArrayList<>()のターゲット・タイプから、制約式'ArrayList<α> → List<Thread>'があります。 削減によって、これは制約式'α <= Thread'になり、次に'α = Thread'になります。
推論プロセス中に、推論変数に対する一連の境界が保持されます。 バインドには、次のいずれかの形式があります。
バインドは、置換の適用後にアサーションがtrueの場合、推論変数置換によって満たされます。 バインドされたfalseは満たされません。
一部の境界は、推論変数を適切な型に関連付けます。 Tを適切な型にします。 α = Tまたは T = αという形式の境界がある場合、Tは αの instantiationであるとします。 同様に、α <: Tという形式の境界がある場合、Tはαの適切な上限であり、T <: αという形式の境界があると、Tはαの適切な下限であるとします。
その他の境界は、2つの推論変数、または推論変数を推論変数を含む型に関連付けます。 このような境界は、S = TまたはS <: Tという形式であり、依存関係と呼ばれます。
G<α1、 ...、 αn> = capture(G<A1、 ...、 An>)という形式の境界は、α1、 ...、 αnが取得変換の結果のプレースホルダであることを示します。 これは、取得変換を適切な型でのみ実行でき、A1、...、Anの推論変数がまだ解決されない可能性があるため必要です。
throws α形式の境界は単なる情報です。可能な場合はチェック例外タイプにならないように、αのインスタンス化を最適化するように解決を指示します。
推論の重要な中間結果は、バインド・セットです。 シンボル trueを持つ emptyバインドセットを参照すると便利な場合もあります。これは便宜上なく、両者は交換可能です。
バインド・セットの例:
{ α = String }には、αをStringとしてインスタンス化する単一のバインドが含まれています。
{ Integer <: α、 Double <: α、 α <: Object }は、2つの適切な下限とαの適切な上限を記述します。
{ α <: Iterable<?>、 β <: Object、 α <: List<β> }は、それぞれαとβの適切な上限と、それらの間の依存性を示します。
{ }には境界も依存関係もなく、trueと呼びます。
{ false }は、満足のいくインスタンス化が存在しないことを表します。
推論が開始されると、通常、型パラメータ宣言 P1、 ...、 Ppおよび関連する推論変数 α1、 ...、 αpのリストからバインドセットが生成されます。 このようなバインド・セットは次のように生成されます。 l (1 ≤ l ≤ p)ごとに、次を実行します。
PlにTypeBoundがない場合、バインドされたαl <: Objectがセットに表示されます。
それ以外の場合、TypeBoundの&で区切られたT型ごとに、バインドされたαl <: T[P1:=α1、...、Pp:=αp]がセットに表示されます。この結果、αl (依存関係のみ)の適切な上限が得られない場合、バインドされたαl <: Objectもセットに表示されます。
削減は、一連の制約式(§18.1.2)を簡略化してバインド・セット(§18.1.3)を生成するプロセスです。
各制約式は順番に考慮されます。 このセクションのルールでは、フォーミュラを以下のいずれかまたは両方に減らす方法を指定します。
バインド・セットまたはバインド・セット。現在のバインド・セットに組み込まれます。 最初は、現在のバインド・セットは空です。
再帰的に削減される追加の制約式。
削減する制約式がこれ以上ない場合、削減は完了します。
削減ステップの結果は常にサウンド保存です。推論変数のインスタンス化が削減された制約および境界を満たす場合、元の制約も満たされます。 一方、縮小は完全保持ではありません。元の制約を満たすが、縮小された制約または制限を満たさない推論変数インスタンスが存在する可能性があります。 これは、アルゴリズムの固有の制限と、過度の複雑さを回避したいという願望によるものです。 1つの効果は、型引数推論が解を見つけることができないが、プログラマが適切な型を明示的に挿入する場合には、式を適切に入力できることです。
'Expression → T'形式の制約式は、次のように縮小されます。
Tが適切な型の場合、式がT (§5.3)との緩い呼出しコンテキストで互換性がある場合はtrueに、それ以外の場合はfalseに減少します。
それ以外の場合、式がS型のスタンドアロン式(§15.2)である場合、制約は'S → T'に縮小されます。
それ以外の場合、式は多式(§15.2)です。 結果は式の形式によって異なります。
式が( 「式」)という形式のカッコで囲まれた式の場合、制約は「式」→「T」に縮小されます。
式がクラス・インスタンス作成式またはメソッド呼出し式である場合、制約は、§18.5.2.1で定義されているターゲット・タイプTとの式の互換性を判断するために使用されるバインド・セットB3に縮小されます。 (クラス・インスタンス作成式の場合、推論に使用される対応するメソッドが§15.9.3で定義されています。)
このバインド・セットには、新しい推論変数、およびこれらの新しい変数とTの推論変数間の依存性を含めることができます。
式がe1 ? e2 : e3という形式の条件式の場合、制約はe2 → Tおよびe3 → Tという2つの制約式に縮小されます。
式がラムダ式またはメソッド参照式の場合、結果は次のように指定されます。
式がe1、...、enの結果式を持つswitch式の場合、制約はn制約式、'e1 → T'、...、'en → T'に縮小されます。
ネストされた汎用メソッド呼出しをポリ式として処理することで、ネストされた呼出しに対する推論の動作が向上します。 たとえば、Java SE 7では次は不正ですが、Java SE 8では有効です。
ProcessBuilder b = new ProcessBuilder(Collections.emptyList()); // ProcessBuilder's constructor expects a List<String>
外部呼出しとネストされた呼出しの両方で推論が必要な場合、問題はより困難になります。 たとえば:
List<String> ls = new ArrayList<>(Collections.emptyList());
この方法では、ネストされた呼出し(emptyListの場合は{ α <: Object }のみ)で推測される境界を外部推論プロセス(この場合は、コンストラクタがArrayList<β>型の場合のβを推測しようとします)に「リフト」します。 また、ネストされた推論変数と外部推論変数の間の依存性も推測します(制約'List<α> → Collection<β>'は、依存性α = βに減少します)。 このようにして、ネストされた呼出しにおける推論変数の解決は、(割当てターゲットに基づくβ = String)外部呼出しから追加情報を推測できるまで待機できます。
'LambdaExpression → T'という形式の制約式(Tは少なくとも1つの推論変数を表します)は、次のように削減されます。
Tが関数型でない場合(§9.8)、制約はfalseに減少します。
それ以外の場合は、T'を§15.27.3で指定されているように、Tから導出された接地ターゲット・タイプにします。 §18.5.3を使用してパラメータ化された関数型を導出する場合、F<A'1、 ...、 A'm>がF<A1、 ...、 Am>のサブタイプであることはテストされません(かわりに、次の制約式でアサートされます)。 ラムダ式のターゲット関数型をT'の関数型にします。 次に、
有効なファンクション・タイプが見つからない場合、制約はfalseに減少します。
それ以外の場合、LambdaExpressionとターゲット関数型が一致すると、次のように表明されます。
ラムダ・パラメータの数がファンクション・タイプのパラメータ・タイプの数と異なる場合、制約はfalseに減少します。
ラムダ式が暗黙的に入力され、1つ以上のファンクション・タイプのパラメータ型が適切な型でない場合、制約はfalseに減少します。
§18.5.1で暗黙的に型指定されたラムダ式が処理され、§18.5.2.2でターゲット型に適用される置換が処理されるため、この条件は実際には発生しません。
関数型の結果がvoidで、ラムダ本体が文式でも無効互換ブロックでもない場合、制約はfalseに減少します。
関数型の結果がvoidではなく、ラムダ本体が値互換性のないブロックである場合、制約はfalseに減少します。
それ以外の場合、制約は次のすべての制約式に縮小されます。
ラムダパラメータが明示的に宣言された型 F1、 ...、 Fnを持ち、関数型にパラメータ型 G1、 ...、 Gnがある場合、(i)すべての i (1 ≤ i ≤ n)、'Fi = Gi'、および(ii) 'T' <: T'の場合。
ファンクション・タイプの戻り型が(void以外)型Rの場合、ラムダのパラメータ型がファンクション・タイプのパラメータ型と同じであると仮定します。 次に、
Rが適切な型で、ラムダ本体またはラムダ本体の一部の結果式がRとの代入コンテキストで互換性がない場合、falseになります。
それ以外の場合、Rが適切な型でない場合、ラムダ本体の形式が式の場合、制約'式 → R'の場合、またはラムダ本体が結果式e1、...、emを持つブロックの場合、すべてのi (1 ≤ i ≤ m)、'ei → R'の場合。
ラムダ式を含む互換性制約から導出する重要な情報は、ターゲット関数型の戻り型に表示される推論変数の範囲のセットです。 関数型インタフェースは一般的であることが多く、これらの型で動作する多くのメソッドも一般的であるため、これは重要です。
最も単純なケースでは、ラムダ式は推論変数の下限を単純に提供できます。
<T> List<T> makeThree(Factory<T> factory) { ... }
String s = makeThree(() -> "abc").get(2);
より複雑なケースでは、結果式は多式(おそらく別のラムダ式)である可能性があるため、バインドが生成される前に、ターゲット・タイプが異なる複数の制約式を介して推論変数が渡される場合があります。
この項で説明する作業のほとんどは、結果式に関するアサーションの前にあります。その目的は、ラムダ式の関数型を導出することと、互換性から明確に修飾されていない式をチェックすることです。
ターゲット・ファンクション・タイプのthrows句に含まれる推論変数の境界を作成しません。 これは、例外の封じ込めが互換性の一部ではない(§15.27.3)ためであり、特に方法の適用性に影響を与えない(§18.5.1)。 ただし、呼出し型推論(§18.5.2.2)では例外包含制約式(§18.2.5)が生成されるため、これらの変数は後でdoによって境界が取得されます。
ターゲット・タイプが推論変数である場合、またはターゲット・タイプのパラメータ・タイプに推論変数が含まれている場合、falseが生成されます。 起動型推論(§18.5.2.2)では、これらの推論変数をインスタンス化するために追加の置換が実行されるため、このシナリオは回避されます。 (つまり、削減は実際には、これらの形式のいずれかのターゲット・タイプで「起動」されることはありません。)
最後に、ラムダ式の結果式は、§15.27.3によって、ターゲット・タイプの戻り型Rとの代入コンテキストで互換性があることが必要であることに注意してください。 Rが適切な型(Functionから導出された<α,Byte>Byteなど)である場合、割当て可能性はテストに十分簡単で、リダクションは前述のようになります。 RがFunctionから導出されたαなど、適切な型でない場合は、ルーズ呼出しの互換性で十分であると仮定します。 代入互換性とルース呼出し互換性の違いは、代入のみが<String,α>Byte b = 100;などの定数式を絞り込むことができる点です。 したがって、単純化の前提は完全性の保持ではありません。つまり、ターゲット戻り型αおよび整数リテラル結果式100が指定された場合、αはByteにインスタンス化できると考えられますが、リダクションによって実際にこのような境界が生成されることはありません。
'MethodReference → T'という形式の制約式。Tでは、少なくとも1つの推論変数が記述されます。
Tが関数型でない場合、またはTが関数型を持たない関数型(§9.9)である場合、制約はfalseに減少します。
それ以外の場合、Tのターゲット指定時にメソッド参照に適用可能なメソッドが存在しない場合、制約はfalseに減少します。
それ以外の場合、メソッド参照が完全(§15.13.1)である場合は、P1、...、PnをTのファンクション・タイプのパラメータ・タイプにし、F1、...、Fkを、適用可能なメソッドのパラメータ・タイプにしてください。 制約は、次のように新しい制約セットに縮小されます。
n = k+1の特殊なケースでは、タイプ P1のパラメータが呼び出しのターゲット参照として機能します。 メソッド参照式の形式は、必ずReferenceType :: [TypeArguments] Identifierになります。 この制約は、'P1 <: ReferenceType'に減り、すべてのi (2 ≤ i ≤ n)、'Pi → Fi-1'に減ります。
その他のすべてのケースでは、n = kであり、すべての i (1 ≤ i ≤ n)、'Pi → Fi')に対して制約が減少します。
関数型の結果がvoidでない場合は、Rをその戻り型にします。 次に、適用可能なコンパイル時宣言の結果がvoidの場合、制約はfalseに減少します。 それ以外の場合、制約は'R' → R'に縮小されます。ここで、Rは、適用可能なコンパイル時宣言の戻り型に取得変換(§5.1.10)を適用した結果です。
それ以外の場合、メソッド参照は正確ではなく、次の処理を実行します。
1つ以上のファンクション・タイプのパラメータ・タイプが適切なタイプでない場合、制約はfalseに減少します。
§18.5.1の不正確なメソッド参照と§18.5.2.2のターゲット・タイプに適用される置換が処理されるため、この条件は実際には発生しません。
それ以外の場合は、§15.13.1で指定されているように、コンパイル時宣言の検索が実行されます。 メソッド参照のコンパイル時宣言がない場合、制約はfalseに減少します。 それ以外の場合は、コンパイル時の宣言があり、次のことを行います(Rを関数型の結果にしてください)。
Rがvoidの場合、制約はtrueに減少します。
それ以外の場合、メソッド参照式でTypeArgumentsが省略され、コンパイル時宣言が汎用メソッドで、コンパイル時宣言の戻り型がメソッドの型パラメータの少なくとも1つを示している場合は、次のようになります。
Rがファンクション・タイプのタイプ・パラメータの1つを示している場合、制約はfalseに減少します。
この場合、Rという制約によって、範囲外型の変数によって結合される推論変数が導かれることがあります。 範囲外の型変数を使用して推論変数をインスタンス化することは非意味的であるため、可能性が発生するたびにすぐにあきらめることで状況を回避することを好みます。 この単純化は完全性保存ではありません。
Rがファンクション・タイプのタイプ・パラメータの1つを指定しない場合、制約はバインド・セットB3に縮小され、これは§18.5.2.1で定義されているように、ファンクション・タイプの戻り型をターゲットとするときにメソッド参照の互換性を判断するために使用されます。 B3には、新しい推論変数、およびこれらの新しい変数とTの推論変数との間の依存性を含めることができます。
汎用参照メソッドの戻り型を決定するために使用される戦略は、この項で汎用メソッド呼出しに使用したパターンに従います。 これには、外部コンテキストへの「リフティング」境界、および2つの推論変数のセット間の依存関係の推測が含まれる場合があります。
それ以外の場合は、R'をコンパイル時宣言の呼出し型(§15.12.2.6)の戻り型に取得変換(§5.1.10)を適用した結果にします。 Rがvoidの場合、制約はfalseに減少し、それ以外の場合、制約は'R' → R'に減少します。
'S → T'形式の制約式は、次のように縮小されます。
SおよびTが適切な型である場合、SがT (§5.3)との緩い起動コンテキストで互換性がある場合、制約はtrueに減少し、それ以外の場合はfalseになります。
それ以外の場合、Sがプリミティブ型の場合は、S'をボクシング変換(§5.1.7)からSに適用した結果にしてください。 次に、制約が「S → T」に減少します。
それ以外の場合は、Tがプリミティブ型の場合、T'をボクシング変換(§5.1.7)を Tに適用した結果にしてください。 次に、制約が「S = T」に減少します。
それ以外の場合、TがG<T1、 ...、 Tn>という形式のパラメータ化された型であり、SのスーパータイプであるG<...>という形式のタイプが存在せず、RAW型GがSのスーパータイプである場合、制約はtrueに減少します。
それ以外の場合、Tが G<T1、 ...、 Tn>[]kという形式の配列型であり、Sのスーパータイプである G<...>[]kという形式のタイプが存在せず、raw型 G[]kが Sのスーパータイプである場合、制約は trueに減少します。 (表記[]kは、kディメンションの配列タイプを示します。)
それ以外の場合、制約は'S <: T'に縮小されます。
4番目と5番目のケースは、チェックされていない変換の暗黙的な使用です(§5.1.9)。 これらは、最初のケースでの未チェック変換の使用とともに、コンパイル時に未チェックの警告が発生し、メソッドの呼出しタイプ(§15.12.2.6)に影響を与える可能性があります。
ボクシング Tから T'は完全保持ではありません。たとえば、Tが longの場合、Sは Integerにインスタンス化される可能性があります。これは Longのサブタイプではありませんが、ボックス化を解除して longに拡大できます。 ほとんどの場合、推論変数戻り型に特別な処置を施すことによって、この問題を避けるには、すでに特定のボックス化プリミティブ型に制約されています。§18.5.2.1を参照してください。
同様に、Tがパラメータ化された型でない場合(たとえば、Tが推論変数である場合)、未チェックの変換の処理は完全性を犠牲にします。 チェックされていない変換が必要かどうかは、通常、そのような状況では明らかではありません。 未チェックの変換では未チェックの警告が発生するため、推測では明確に必要でないかぎり、警告を回避することを推奨します。
'S <: T'形式の制約式は、次のように縮小されます。
Sと Tが適切な型である場合、Sが T (§4.10)のサブタイプである場合、制約は trueに減少し、それ以外の場合は false。
それ以外の場合、Sがnull型の場合、制約はtrueに減少します。
それ以外の場合、Tがnull型の場合、制約はfalseに減少します。
それ以外の場合、Sが推論変数αの場合、制約はバインドされたα <: Tに減少します。
それ以外の場合、Tが推論変数αの場合、制約はバインドされたS <: αに減少します。
それ以外の場合、制約は Tの形式に従って減少します。
Tがパラメータ化されたクラスまたはインタフェース型、あるいはパラメータ化されたクラスまたはインタフェース型の内部クラス型(直接的または間接的)の場合、A1、 ...、 Anを Tの型引数にしてください。 Sのスーパータイプの中で、型引数 B1、 ...、 Bnを使用して、対応するクラスまたはインタフェース型が識別されます。 そのようなタイプが存在しない場合、制約はfalseに減少します。 それ以外の場合、この制約は、すべての i (1 ≤ i ≤ n)、'Bi <= Ai'の新しい制約に縮小されます。
Tが他のクラスまたはインタフェース・タイプである場合、TがSのスーパータイプに含まれている場合はtrueに、それ以外の場合はfalseに減少します。
Tが配列型T'[]の場合、配列型であるSのスーパータイプの中で、最も具体的な型S'[] (S自体の場合もあります)が識別されます。 このような配列タイプが存在しない場合、制約はfalseに減少します。 そうでない場合は、次のようになります。
Sと Tのどちらもプリミティブ型でない場合、制約は'S <: T''に縮小されます。
それ以外の場合、制約はtrue (S'とT'が同じプリミティブ型の場合)に減少し、それ以外の場合はfalseになります。
Tが型変数の場合、次の3つのケースがあります。
SがTが要素である交差タイプの場合、制約はtrueに減少します。
それ以外の場合、Tに下限 Bがあると、制約は「S <: B」に減少します。
それ以外の場合、制約はfalseに減少します。
Tが交差タイプ I1 & ... & Inの場合、この制約は、すべての i (1 ≤ i ≤ n)、'S <: Ii'の新しい制約に縮小されます。
'S <= T'という形式の制約式。ここで、SおよびTは型引数(§4.5.1)です。
Tが型の場合:
Sが型の場合、制約は「S = T」に減少します。
Sがワイルドカードの場合、制約はfalseに減少します。
Tが?形式のワイルドカードの場合、制約はtrueに減少します。
Tが ? extends T'形式のワイルドカードの場合:
Sが型の場合、制約は'S <: T''に縮小されます。
Sが?形式のワイルドカードである場合、制約は'Object <: T''に縮小されます。
Sが? extends S形式のワイルドカードである場合、制約は'S' <: T''に縮小されます。
Sが? super S形式のワイルドカードである場合、制約は'Object = T''に縮小されます。
Tが ? super T'形式のワイルドカードの場合:
Sが型の場合、制約はT <: Sに縮小されます。
Sが? super S形式のワイルドカードの場合、制約はT <: Sに縮小されます。
それ以外の場合、制約はfalseに減少します。
S = Tという形式の制約式(Sと Tは型)は、次のように減少します。
SおよびTが適切な型である場合、SがT (§4.3.4)と同じである場合、制約はtrueに減少し、それ以外の場合はfalseになります。
それ以外の場合、SまたはTがNULL型の場合、制約はfalseに減少します。
それ以外の場合、Sが推論変数 αで、Tがプリミティブ型でない場合、制約はバインドされた α = Tに縮小されます。
それ以外の場合、Tが推論変数 αで、Sがプリミティブ型でない場合、制約はバインドされた S = αに縮小されます。
それ以外の場合、Sと Tが同じイレイジャを持つクラスまたはインタフェースタイプで、Sには型引数 B1、...、Bn、および Tに型引数 A1、...、Anがある場合、制約は、すべての i (1 ≤ i ≤ n)、'Bi = Ai'の新しい制約に縮小されます。
それ以外の場合、SおよびTが配列型、S[]およびT[]の場合、制約は'S' = T''に減少します。
それ以外の場合、Sと Tが交差型であれば、Sの要素と Tの要素との間の対応関係が確立されます。 S、Siの要素は、T、Tjの要素に対応します(Siと Tjが同じ型であるか、または同じ汎用クラスまたはインタフェースのパラメータ化の両方であるか、あるいは両方の配列型であるか)。
Sの各要素が Tの1つの要素に相当し、その逆も同様である場合、制約は、Sの各要素 Sおよび Tiの対応する要素 Tに対して、Si = Tjの新しい制約に減少します。 そうでない場合、制約はfalseに減少します。
このルールは、(パラメータ化された型でネストされるのではなく)交差型の要素として直接表示される推論変数には対応しません。 型パラメータ宣言(§4.4)の制限により、このような交差型は実際には発生しません。
それ以外の場合、制約はfalseに減少します。
'S = T'という形式の制約式。ここで、Sと Tは型引数(§4.5.1)です。
Sと Tが型の場合、制約は前述のように縮小されます。
Sの形式が?で、Tの形式が?の場合、制約はtrueに減少します。
Sの形式が?で、Tの形式が? extends Tの場合、制約は'Object = T''に縮小されます。
Sの形式が? extends Sで、Tの形式が?の場合、制約は'S' = Object'に縮小されます。
Sの形式が? extends Sで、Tの形式が? extends Tの場合、制約は'S' = T''に縮小されます。
Sの形式が? super Sで、Tの形式が? super Tの場合、制約は'S' = T''に縮小されます。
それ以外の場合、制約はfalseに減少します。
'LambdaExpression →throws T'形式の制約式は、次のように短縮されます。
Tが関数型でない場合(§9.8)、制約はfalseに減少します。
それ以外の場合は、§15.27.3で指定されているように、ラムダ式のターゲット関数型を決定してください。 有効なファンクション・タイプが見つからない場合、制約はfalseに減少します。
それ以外の場合、ラムダ式が暗黙的に入力され、1つ以上の関数型のパラメータ型が適切な型でないと、制約はfalseに減少します。
この条件は、§18.5.2.2でターゲット・タイプに適用される置換のため、実際には発生しません。
それ以外の場合、ファンクション・タイプの戻り型がvoidでも適切な型でもない場合、制約はfalseに減少します。
この条件は、§18.5.2.2でターゲット・タイプに適用される置換のため、実際には発生しません。
それ以外の場合は、E1、...、Enを、適切な型ではないファンクション・タイプのthrows句の型にしてください。 ラムダ式が暗黙的に入力されている場合は、そのパラメータ型を関数型のパラメータ型にしてください。 ラムダ本体がポリ式またはポリ結果式を含むブロックの場合、対象となる戻り型を関数型の戻り型にしてください。 X1、 ...、 Xmを、ラムダ本体がスローできるチェック例外タイプにします(§11.2)。 その次には、2つのケースがあります:
n = 0 (ファンクション・タイプのthrows句が適切な型のみで構成される)の場合、i (1 ≤ i ≤ m)が存在し、Xiがthrows句の適切な型のサブタイプでない場合、制約はfalseに減少し、存在しない場合、制約はtrueに減少します。
n > 0の場合、制約はサブタイピング制約のセットに縮小されます。すべての i (1 ≤ i ≤ m)について、Xiが throws句内のどの適切なタイプのサブタイプでもない場合、制約にはすべての j (1 ≤ j ≤ n)、'Xi <: Ej'が含まれます。 さらに、すべての j (1 ≤ j ≤ n)について、制約はバインドされた throws Ejに縮小されます。
'MethodReference →throws T'という形式の制約式は、次のように短縮されます。
Tが関数型でない場合、またはTが関数型であるが関数型を持たない場合(§9.9)、制約はfalseに減少します。
それ以外の場合は、メソッド参照式のターゲット関数型をTの関数型にします。 メソッド参照が不正確(§15.13.1)で、1つ以上のファンクション・タイプのパラメータ型が適切な型でない場合、制約はfalseに減少します。
それ以外の場合、メソッド参照が不正確で、ファンクション・タイプの結果がvoidでも適切な型でもない場合、制約はfalseに減少します。
それ以外の場合は、E1、...、Enを、適切な型ではないファンクション・タイプのthrows句の型にしてください。 X1、 ...、 Xmを、メソッド参照のコンパイル時宣言(§15.13.2)の呼出しタイプのthrows句(ファンクション・タイプのパラメータ・タイプおよび戻り型から導出)でチェックされた例外にします。 その次には、2つのケースがあります:
n = 0 (ファンクション・タイプのthrows句が適切な型のみで構成される)の場合、i (1 ≤ i ≤ m)が存在し、Xiがthrows句の適切な型のサブタイプでない場合、制約はfalseに減少し、存在しない場合、制約はtrueに減少します。
n > 0の場合、制約はサブタイピング制約のセットに縮小されます。すべての i (1 ≤ i ≤ m)について、Xiが throws句内のどの適切なタイプのサブタイプでもない場合、制約にはすべての j (1 ≤ j ≤ n)、'Xi <: Ej'が含まれます。 さらに、すべての j (1 ≤ j ≤ n)について、制約はバインドされた throws Ejに縮小されます。
戻り型の互換性はメソッドの適用性に影響しますが(§18.5.1)、例外はオーバーロード解決の完了後の呼出しタイプにのみ影響するため(§18.5.2)、チェック例外に対する制約は戻り型の制約とは別に処理されます。 これは、ラムダ式の互換性(§15.27.3)の定義に例外の互換性を含めることで簡略化できますが、明示的に型指定されたラムダ本体によってスローされる可能性のある例外が過負荷の解決を変える場合には、驚くことになる可能性があります。
ラムダ本体によってスローされた例外は、(i)ラムダのパラメータ型が既知で、(ii)本体内の結果式のターゲット型が既知になるまで特定できません。 (2番目の要件は、たとえば、戻り型とthrows句に同じ型パラメータが存在する一般的なメソッド呼出しを考慮することです。) したがって、ターゲット・タイプTから導出されたこれらの両方を適切なタイプにする必要があります。
1つの結果として、他のラムダ式から戻されたラムダ式は、スローされた例外から制約を生成できないことが挙げられます。 これらの制約は、トップレベルのラムダ式からのみ生成できます。
ファンクション・タイプのthrows句に複数の推論変数が出現するケースの処理は、完全性の保持ではありません。 どちらの変数も、単独で、チェックされた各例外を宣言する制約を満たすことができますが、どの例外が意図されているのかは確認できません。 そのため、予測可能性のために、両方を制約します。
バインド・セットは推論中に生成および拡張されるため、元の境界のアサーションに基づいて新しい境界を推測できます。 incorporationのプロセスは、これらの新しい境界を識別し、それらをバインドされたセットに追加します。
結合は、2つのシナリオで発生する可能性があります。 1つのシナリオは、バインド・セットに境界の補完的なペアが含まれていることです。これは、§18.3.1で指定された新しい制約式を意味します。 もう1つのシナリオは、バインドされたセットに取得変換を含むバインドが含まれていることです。これは、新しい境界を意味し、§18.3.2で規定されているように、新しい制約式を意味する場合があります。 どちらのシナリオでも、新しい制約式が削減され、バインドされたセットに新しい境界が追加されます。 これにより、さらに組み込まれる可能性があります。最終的には、セットが固定ポイントに到達し、それ以上の境界を推測することはできません。
バインド・セットの結合が固定ポイントに達し、そのセットにバインドされたfalseが含まれていない場合、バインド・セットには次のプロパティがあります。
適切な下限Lと推論変数の適切な上限Uの組合せごとに、L <: Uを使用します。
バインドによって指定されたすべての推論変数にインスタンス化がある場合、そのバインドは対応する置換によって満たされます。
依存関係 α = βの場合、αのすべての境界は βの境界に一致し、その逆も同様です。
依存性α <: βの場合、αの下限はすべてβの下限で、βの上限はすべてαの上限です。
結合するアサーションが固定ポイントに達すると、問題がわずかに単純化されます。 On Decidability of Nominal Subtyping with VarianceのKennedy and Pierceの作業に基づいて、このプロパティは、バインドされたセットに表示される可能性のある一連の型が有限であることを引数にすることで証明できます。 引数は、次の2つの仮定に依存します。
サブタイピング制約を減らす場合、新しいキャプチャ変数は生成されません(§18.2.3)。
拡張継承パスは追跡されません。
この仕様では、現在、これらのプロパティが保証されていません(サブタイピング制約を減らす場合のワイルドカードの処理は不正確であり、拡張継承パスは検出されません)。ただし、将来のバージョンでは保証される可能性があります。 (これは新しい問題ではありません。Javaサブタイピング・アルゴリズムも終了しないリスクがあります。)
(このセクションでは、Sと Tは推論変数または推論型であり、Uは適切な型です。 簡潔にするために、α = Tという形式の境界も、T = αという形式の境界と一致する場合があります。)
バインドされたセットに、次のいずれかのルールに一致する境界のペアが含まれている場合、新しい制約式が暗黙的に示されます。
α = S、α = Tは'S = T'を意味します。
α = S、α <: Tは'S <: T'を意味します。
α = S、T <: αは'T <: S'を意味します。
S <: αおよびα <: Tは'S <: T'を意味します。
α = U、S = Tは'S[α:=U] = T[α:=U]'を意味します。
α = U、S <: Tは'S[α:=U] <: T[α:=U]'を意味します。
When a bound set contains a pair of bounds α <: S and α <: T, and there exists a supertype of S of the form G<S1, ..., Sn> and a supertype of T of the form G<T1, ..., Tn> (for some generic class or interface, G), then for all i (1 ≤ i ≤ n), if Si and Ti are types (not wildcards), the constraint formula ‹Si = Ti› is implied.
バインド・セットにG<α1、 ...、 αn> = capture(G<A1、 ...、 An>という形式の境界が含まれている場合、次のように新しい境界が暗黙的に指定され、新しい制約式が暗黙的に指定されます。
P1、 ...、 Pnは Gの型パラメータを表し、B1、 ...、 Bnはこれらの型パラメータの境界を表します。 θが置換[P1:=α1、 ...、 Pn:=αn]を表すようにします。 Rは推論変数ではない型にします(ただし、必ずしも適切な型ではありません)。
α1、 ...、 αn上の一連の境界が暗黙的に指定され、P1、 ...、 Pnの宣言された境界から生成されます(§18.1.3を参照)。
さらに、すべての i (1 ≤ i ≤ n)の場合:
Aiがワイルドカードでない場合、バインドされた αi = Aiが暗黙的に指定されます。
Aiが?形式のワイルドカードの場合:
αi = Rは、バインドされたfalseを意味します
αi <: Rは制約式'Bi θ <: R'を意味します。
R <: αiは、バインドされたfalseを意味します。
Aiが? extends T形式のワイルドカードの場合:
αi = Rは、バインドされたfalseを意味します
BiがObjectの場合、αi <: Rは制約式'T <: R'を意味します。
TがObjectの場合、αi <: Rは制約式'Bi θ <: R'を意味します。
R <: αiは、バインドされたfalseを意味します。
Aiが? super T形式のワイルドカードの場合:
αi = Rは、バインドされたfalseを意味します
αi <: Rは制約式'Bi θ <: R'を意味します。
R <: αiは、制約式'R <: T'を意味します。
バインドされたfalseを含まないバインドされたセットの場合、バインドされたセットによって示される推論変数のサブセットが解決される場合があります。 これは、要求されたすべての変数にインスタンス化があるまで、各推論変数のセットに十分なインスタンス化を追加できることを意味します。
バインド・セット内の依存関係では、変数を特定の順序で解決するか、追加の変数を解決する必要がある場合があります。 依存関係は次のように指定されます。
次のいずれかの形式の境界があるとします。ここで、Tは推論変数βまたはβを表す型です。
α = T
α <: T
T = α
<: α
αが G<...、 α、 ...> = capture(G<...>)という形式の別の境界の左側に存在する場合、βは αの分解能に依存します。 それ以外の場合、αはβの解像度に依存します。
G<...、 α、 ...> = capture(G<...>)という形式の境界の左側に表示される推論変数αは、この境界(= 記号の両側)に記載されている他のすべての推論変数の解像度によって異なります。
推論変数αは、αがγの分解能に依存するように推論変数γが存在する場合、推論変数βの分解能に依存し、γがβの分解能に依存します。
推論変数 αは、それ自体の分解能に依存します。
解決する一連の推論変数を指定した場合、Vは、このセットの和集合と、このセット内の少なくとも1つの変数の解決が依存するすべての変数になります。
V内のすべての変数にインスタンス化がある場合、解決は成功し、このプロシージャーは終了します。
それ以外の場合、α1、 ...、 αn }を V内のインスタンス化されていない変数の空でないサブセットにし、すべての i (1 ≤ i ≤ nの(αiが次に依存する場合)変数βの解決、βにインスタンス化があるか、β = αjになるようなjがあるか、(ii)このプロパティを持つ{ α1、 ...、 αn }の空でない適切なサブセットが存在しない。 解決は、バインドされたセット内の境界に基づいて、α1、 ...、 αnの各インスタンス化を生成することによって続行されます。
バインドセットに G<...、αi、...> = capture(G<...>)という形式の境界が含まれていない場合、すべての i (1 ≤ i ≤ n)の候補インスタンス化 Tiは、αiごとに定義されます。
境界 α1 = T1、 ...、 αn = Tnは、現在の境界セットに組み込まれています。
結果にバインドされたfalseが含まれていない場合、結果は新しいバインド・セットになり、前述のように(必要に応じて)インスタンス化する新しい変数のセットを選択して解決が続行されます。
それ以外の場合、結果にはバインドされたfalseが含まれるため、次のステップを実行して{ α1、 ...、 αn }をインスタンス化するために2回目の試行が行われます。
バインドセットに G<...、αiという形式の境界が含まれている場合、...> = capture(G<...>) (1 ≤ i ≤ n)または;
前述のステップで生成されたバインド・セットにバインドされたfalseが含まれている場合。
次に、Y1、 ...、 Ynを、次のような境界を持つ新しい型変数にします。
すべての i (1 ≤ i ≤ n)について、αiに1つ以上の proper下限 L1、 ...、 Lkがある場合、Yiの下限をlub(L1、 ...、 Lk)にします。そうでない場合は、Yiに下限はありません。
すべてのi (1 ≤ i ≤ n)で、αiには上限U1、...、Uk、Yiの上限をglb(U1 θ、...、Uk θ)にします。ここで、θは置換[α1:=Y1、...、αn:=Yn].
型変数 Y1、 ...、 Ynに整形式の境界がない場合(つまり、下限が上限のサブタイプでないか、交差タイプが矛盾している)、解決は失敗します。
それ以外の場合は、すべての i (1 ≤ i ≤ n)の場合、G<...という形式のすべての境界、αiという形式のすべての境界、...> = capture(G<...>)が現在の境界セットから削除され、境界 α1 = Y1、...、αn = Ynが組み込まれます。
結果にバインドされたfalseが含まれていない場合、結果は新しいバインド・セットになり、前述のように(必要に応じて)インスタンス化する新しい変数のセットを選択して解決が続行されます。
それ以外の場合、結果にはバインドされたfalseが含まれ、解決に失敗します。
推論変数をインスタンス化する最初の方法は、その変数の境界からインスタンス化を導出します。 ただし、複雑な依存関係は、結果が変数の範囲内にないことを意味します。 この場合、異なるインスタンス化方法が実行され、変換の取得に似ています(§5.1.10)。新しい型変数が導入され、推論変数の境界から導出されます。 これらの「取得」変数の下限は、適切な型のみを使用して計算されることに注意してください。これは、インスタンス化されていない型変数に対して型計算を実行しようとしないようにするために重要です。
前述の推論プロセスを使用して、コンパイル時に次の分析が実行されます。
明示的な型引数を提供しないメソッド呼出しの場合、適用可能な汎用メソッドmが適用可能かどうかを判断するプロセスは、次のとおりです。
ここで、P1、 ...、 Pp (p ≥ 1)は mの型パラメータで、α1、 ...、 αpは推論変数であり、θは置換 [P1:=α1、 ...、 Pp:=αp]になります。
初期バインド・セットB0は、P1、 ...、 Ppの宣言された境界から生成されます(§18.1.3を参照)。
すべての i (1 ≤ i ≤ p)について、Piが mの throws句に含まれている場合は、バインドされた throws αiが暗黙的に指定されます。 これらの境界がある場合は、B0に組み込まれて、新しいバインドセット B1が生成されます。
制約式Cのセットは次のように生成されます。
F1、...、Fnをmの仮パラメータ型とし、e1、...、ekを呼出しの実際の引数式にします。 次に、
厳密な呼出しによる適用性をテストするには:
k ≠ nの場合、またはi (1 ≤ i ≤ n)が存在し、eiが適用性(§15.12.2.2)に関連し、(i) eiがスタンドアロン式である場合プリミティブ型ですが、Fiは参照型であるか、(ii) Fiはプリミティブ型ですが、eiはプリミティブ型のスタンドアロン式ではありません。その後、メソッドは適用できず、推論を続行する必要がありません。
それ以外の場合、Cは、eiが適用性に関連しているすべての i (1 ≤ i ≤ k)について、'ei → Fi θ'を含めます。
ルース呼出しによる適用性をテストするには:
k≠nの場合、メソッドは適用できず、推論を続行する必要はありません。
それ以外の場合、Cは、eiが適用性に関連しているすべての i (1 ≤ i ≤ k)について、'ei → Fi θ'を含めます。
可変アリティ呼出しによる適用性をテストするには:
F'1、 ...、 F'kを、mの最初のk変数引数の型(§15.12.2.4)とします。 Cは、eiが適用性に関連しているすべての i (1 ≤ i ≤ k)について、'ei → F'i θ')を含みます。
Cは縮小され(§18.2)、結果の境界は B1に組み込まれ、新しいバインドセット B2が生成されます。
最後に、B2にfalseのバインドおよびB2のすべての推論変数の解決(§18.4)が含まれていない場合に、メソッドmを適用できます。
次のメソッドの呼出しと割当てについて考えてみます。
List<Number> ln = Arrays.asList(1, 2.0);
呼出しに最も適用可能なメソッドは、§15.12の説明に従って識別する必要があります。 唯一適用可能なメソッド(§15.12.2.1)は、次のように宣言されています。
public static <T> List<T> asList(T... a)
簡単に言えば、このメソッドは厳密な呼出し(§15.12.2.2)にも適用されず、緩い呼出し(§15.12.2.3)にも適用されません。 ただし、他の候補者がいないため、第3フェーズでは、メソッドが可変アリティ呼出しによる適用性をチェックされます。
初期バウンドセット Bは、単一の推論変数 αの簡単な上限です。
{ α <: Object }
初期制約式セットは次のとおりです。
{ '1 → α'、 '2.0 → α' }
これらは、新しいバインド・セットB1に縮小されます。
{α <: Object、 Integer <: α、 Double <: α }
次に、メソッドが適用可能かどうかをテストするために、これらの境界の解決を試みます。 私たちは成功し、かなり複雑なインスタンス化を生み出します
α = Number & Comparable<? extends Number & Comparable<?>>
この方法は適用可能であることを示した。 それでも、次の項で説明するように、メソッド呼出しのタイプと割当てのターゲット・タイプとの互換性は、さらに推論が行われるまで決定されません。
明示的な型引数を提供しないメソッド呼出し式と、対応する最も適用可能な汎用メソッドmを指定した場合、選択したメソッドの呼出しタイプ(§15.12.2.6)を推測するプロセスでは、ターゲット型との互換性を表明するため、およびメソッド呼出しの引数式の妥当性を表明するため、追加の制約を解決する必要がある場合があります。
推論の複数のラウンドが、メソッド呼出しのタイプの検索に関与することに注意してください。 これは、たとえば、ターゲット・タイプが適用可能なメソッドの選択に影響を与えずに呼出しのタイプに影響を与えるようにするために必要です。 第1ラウンド(§18.5.1)では、バインドされたセットが生成され、解決が存在するが、その解決にコミットしないことがテストされます。 その後のラウンドでは、最終的な解決ステップによって式の実際の型が決定されるまで、追加の制約が削減されます。
メソッド呼出し式が多式(§15.12)の場合、ターゲット・タイプTとの互換性は、次のように決定されます。
メソッド呼出し式が厳密な呼出しコンテキストに現れ、Tがプリミティブ型の場合、その式はTと互換性がありません。
そうでない場合は、次のようになります。
B2を、mが§18.5.1で適用可能であることを実証するために、削減によって生成されるバインド・セットにします。
(§18.5.1では、B2の推論変数が解決できることを示すために必要でしたが、適用性を確立するために、この解決ステップによって生成されるインスタンス化は、B2の一部とみなされていません。)
次のように、B3をB2から導出されたバインド・セットにします。
Rをmの戻り型とし、θを[P1:=α1、 ...、 Pp:=αp]に置き換えて、mの型パラメータを推論変数に置き換え、Tを起動のターゲット型にします。 次に、
§18.5.1の制約セットの削減中にメソッドを適用するために未チェックの変換が必要な場合は、制約式'|R| → T'が削減され、B2に組み込まれます。
Otherwise, if R θ is a parameterized type, G<A1, ..., An>, and one of A1, ..., An is a wildcard, then, for fresh inference variables β1, ..., βn, the constraint formula ‹G<β1, ..., βn> → T› is reduced and incorporated, along with the bound G<β1, ..., βn> = capture(G<A1, ..., An>), with B2.
それ以外の場合、R θが推論変数 αで、次のいずれかが当てはまります。
Tは参照型ですが、ワイルドカード・パラメータ型ではありません。また、(i) B2には、α = SまたはS <: αのいずれかの形式のバインドが含まれています。ここで、Sはワイルドカード・パラメータ型です。または(ii) B2には、S1 <: αと S2 <: αという形式の2つの境界が含まれています。ここで、S1と S2には、同じ汎用クラスまたはインタフェースの2つの異なるパラメータ化であるスーパータイプがあります。
Tは汎用クラスまたはインタフェースGのパラメータ化であり、B2はα = SまたはS <: αのいずれかの形式のバインドを含み、S<...>という形式のスーパータイプはSですが、RAW型|G<...>|はSのスーパータイプです。
Tはプリミティブ型で、§5.1.7で言及されているプリミティブ・ラッパー・クラスの1つは、B2のαのインスタンス化、上限または下限です。
αは B2で解決され、結果として得られる αのインスタンス化の取得が Uである場合、制約式'U → T'が削減され、B2に組み込まれます。
それ以外の場合、制約式'R θ → T'が削減され、B2に組み込まれます。
B3にfalseのバインドおよびB3のすべての推論変数の解決(§18.4)が含まれていない場合、メソッド呼出し式はTと互換性があります。
前の項の例を考えてみます。
List<Number>ln = Arrays.asList(1, 2.0);
最も適用可能な方法は、次のように識別されました。
public static<T>List<T>asList(T... a)
メソッド呼出しのタイプ・チェックを完了するには、そのメソッドがターゲット・タイプListと互換性があるかどうかを判断する必要があります。
<Number>
前の項B2の適用性を示すために使用されるバインド・セットは、次のとおりです。
{α <: Object、 Integer <: α、 Double <: α }
新しい制約式セットは次のとおりです。
{ ‹List → <α>List› }
<Number>
この互換性制約は、新しいバインドセット B3に含まれる αの等価境界を生成します。
{ α <: Object、Integer <: α、Double <: α、α = Number }
これらの境界はわずかに解決されます。
α = Number
最後に、asListの宣言された戻り型に対して置換を実行して、メソッド呼出しのタイプがListであることを判別します。これは、ターゲット型との互換性が明確です。
<Number>
この推論戦略は、前の項で説明したように、Java®言語仕様のJava SE 7版とは異なります。この仕様では、(呼出しのターゲット・タイプを考慮する前であっても)下限に基づいてαがインスタンス化されています。 結果の型はListのサブタイプではないため、これによって型エラーが発生します。
<Number>
B2に表示される境界に基づいて、様々な特殊な状況下で、呼出しの戻り型として表示される推論変数を積極的に解決します。 これは、通常の制約'R θ → T'が完全性の保持ではない不幸な状況を回避するためです。 残念ながら、変数を熱心に解決することによって、後で推測される境界を使用することができない可能性があります。 場合によっては、後で呼出し引数から推測される境界(暗黙的に型指定されたラムダ式など)が、B2に存在していた場合、結果が異なる可能性もあります。 これらの制限にもかかわらず、この戦略では一般的なユース・ケースで妥当な結果が得られ、Java®言語仕様のJava SE 7エディションのアルゴリズムとの下位互換性があります。
選択したメソッドの呼出しタイプは、次のように、メソッド呼出し式の引数式によって暗黙的に指定できる追加の制約を考慮した後に決定されます。
メソッド呼出し式が多式の場合、B3を§18.5.2.1で生成されたバインド・セットにして、メソッド呼出しの実際のターゲット・タイプとの互換性を実証します。
メソッド呼出し式が多式でない場合、B3は、mが§18.5.1で適用可能であることを実証するために、リダクションによって生成されるバインド・セットと同じにしてください。
(§18.5.1および§18.5.2.1で、バインド・セット内の推論変数を解決できることを示すために必要でしたが、これらの解決ステップによって生成されるインスタンス化は、B3の一部とみなされていません。)
制約式Cのセットは次のように生成されます。
e1、...、ekを、メソッド呼出し式の実際の引数式にします。
mが厳密な呼出しまたは緩い呼出しによって適用可能な場合は、F1、...、Fkをmの仮パラメータ型にします。mが可変アリティ呼出しによって適用可能な場合は、F1、...、Fkを最初のk変数アリティ・パラメータ型m (§15.12.2.4)にします。
θを置換[P1:=α1、 ...、 Pp:=αp]とし、§18.5.1で定義されたmの型パラメータを推論変数に置き換えます。
次に、すべての i (1 ≤ i ≤ k)の場合:
eiが適用性に関連しない場合、Cには'ei → Fi θ'が含まれます。
追加の制約は、eiの形式に応じて含めることができます。
eiがLambdaExpressionの場合、Cには'LambdaExpression →throws Fi θ'が含まれ、ラムダ本体で追加の制約が検索されます。
ブロック・ラムダ本体の場合、検索は各結果式に再帰的に適用されます。
ポリ・クラス・インスタンス作成式またはポリ・メソッド呼出し式の場合、Cには、ポリ式の起動タイプを推測するときに§18.5.2によって生成されるセットCに表示されるすべての制約式が含まれます。
カッコで囲まれた式の場合、検索は含まれている式に再帰的に適用されます。
条件式の場合、検索は2番目と3番目のオペランドに再帰的に適用されます。
ラムダ式の場合、検索はラムダ本体に再帰的に適用されます。
switch式の場合、検索は各結果式に再帰的に適用されます。
eiがMethodReferenceの場合、Cには'MethodReference →throws Fi θ'が含まれます。
eiがポリ・クラス・インスタンス作成式またはポリ・メソッド呼出し式の場合、Cには、ポリ式の呼出しタイプを推測するときに§18.5.2によって生成されるセットCに表示されるすべての制約式が含まれます。
eiがカッコで囲まれた式の場合、これらのルールは、含まれている式に再帰的に適用されます。
eiが条件式の場合、これらのルールは2番目と3番目のオペランドに再帰的に適用されます。
eiがswitch式の場合、これらのルールは各結果式に再帰的に適用されます。
Cは空ではありませんが、バインドセット B3から始めて、新しい境界を「現在の」境界セットに累積し、最終的には新しい境界セット B4を生成して、次のプロセスが繰り返されます。
制約のサブセットは Cで選択され、各制約について、入力変数が C内のほかの制約の出力変数に影響を与えないというプロパティーが満たされます。 入力変数および出力変数という用語は、次に定義します。 推論変数αは推論変数β(αがβ(§18.4)の解像度に依存する場合、またはその逆の場合)、またはαがγおよびγに影響を与えることができる3番目の推論変数γが存在する場合、βに影響を与える可能性があります。
このサブセットが空の場合、制約間の依存性のグラフにサイクル(またはサイクル)があります。 この場合、依存関係サイクル(またはサイクル)に参加し、サイクル外(またはサイクル)の制約に依存しない C内の制約が考慮されます。 次のように、これらの考慮された制約から単一の制約が選択されます。
考慮される制約のいずれかの形式が'Expression → T'の場合、選択した制約は、この形式の他のすべての考慮される制約の式の左側(§3.5)への式を含む、この形式の制約とみなされます。
「式」→「T」という形式を持つ制約が考慮されない場合、選択した制約は、他のすべての考慮される制約の式の左側にある式を含む制約とみなされます。
選択した制約がCから削除されます。
選択したすべての制約の入力変数 α1、 ...、 αmが解決されます。
ここで、T1、 ...、 Tmは α1、 ...、 αmのインスタンス化であり、置換 [α1:=T1、 ...、 αm:=Tm]はすべての制約に適用されます。
置換によって生じる制約が削減され、現在のバインド・セットに組み込まれます。
最後に、B4にバインドされたfalseが含まれていない場合は、B4の推論変数が解決されます。
インスタンス化 T1、 ...、 Tp (推論変数 α1、 ...、 αp)で解決に成功した場合、θを置換 [P1:=T1、 ...、 Pp:=Tp]にします。 次に、
§18.5.1の制約セットの削減中にメソッドを適用するために未チェックの変換が必要であった場合、θを適用してmの起動タイプのパラメータ型が取得されます。mの型のパラメータ型、およびmの起動型の戻り型とスロー型は、mの型の戻り型とスロー型の消去によって指定されます。
メソッドを適用するために選択されていない変換が不要な場合は、mの型にθを適用することで、mの起動型が取得されます。
B4にバインドされたfalseが含まれている場合、または解決に失敗した場合は、コンパイル時にエラーが発生します。
追加の引数制約を減らすプロセスでは、'Expression → T'、'LambdaExpression →throws T'および'MethodReference →throws T'の形式の制約式を慎重に順序付けする必要があります。 この順序付けを容易にするために、これらの制約の入力変数は次のように定義されます。
'LambdaExpression → T'の場合:
Tが推論変数である場合、それは(唯一の)入力変数です。
Tが関数型であり、関数型が T (§15.27.3)から派生できる場合、入力変数には(i)が含まれます(ラムダ式が暗黙的に型指定されている場合、関数型のパラメータ型によって記述される推論変数。また、(ii)関数型の戻り型Rがvoidでない場合、ラムダ本体(または式の場合は本体自体)の各結果式eについて、'e → R'の入力変数。
それ以外の場合、入力変数はありません。
'LambdaExpression →throws T'の場合:
Tが推論変数である場合、それは(唯一の)入力変数です。
Tが関数型であり、§15.27.3で説明されているように関数型を導出できる場合、入力変数には、(i)ラムダ式が暗黙的に入力されている場合、関数型のパラメータ型によって記述される推論変数、および(ii)関数型の戻り型によって記述される推論変数が含まれます。
それ以外の場合、入力変数はありません。
'MethodReference → T'の場合:
Tが推論変数である場合、それは(唯一の)入力変数です。
Tが関数型を持つ関数型であり、メソッド参照が不正確(§15.13.1)である場合、入力変数は関数型のパラメータ型によって記述される推論変数です。
それ以外の場合、入力変数はありません。
'MethodReference →throws T'の場合:
Tが推論変数である場合、それは(唯一の)入力変数です。
Tが関数型を持つ関数型であり、メソッド参照が不正確(§15.13.1)である場合、入力変数は関数型のパラメータ型および関数型の戻り型によって記述される推論変数です。
それ以外の場合、入力変数はありません。
'Expression → T'の場合、Expressionがカッコで囲まれた式の場合:
含まれる式の式が式の場合、入力変数は'式' → T'の入力変数です。
'ConditionalExpression → T'の場合:
条件式の形式がe1 ? e2 : e3の場合、入力変数は'e2 → T'および'e3 → T'の入力変数です。
'SwitchExpression → T'の場合:
switch式の結果式がe1、...、enの場合、入力変数は、すべてのi (1 ≤ i ≤ n)の入力変数、'ei → T'の入力変数です。
他のすべての制約式には、入力変数はありません。
これらの制約の出力変数はすべて、制約の右側の型(T)で指定される推論変数で、入力変数ではありません。
明示的なパラメータ型が P1、...、Pnのラムダ式が関数型 F<A1、...、少なくとも1つのワイルドカード型引数を持つ Am>をターゲットとしている場合、Fのパラメータ化は、次のようにラムダ式の接地ターゲット型として派生できます。
Q1、 ...、 Qkを、F<α1、 ...、 αm>型の関数型のパラメータ型にします。ここで、α1、 ...、 αmは新しい推論変数です。
n ≠ kの場合、有効なパラメータ化は存在しません。 それ以外の場合、すべての i (1 ≤ i ≤ n)、'Pi = Qi')について、一連の制約式が形成されます。 この制約式セットは、バインドされたセットBを形成するために縮小されます。
Bにバインドされたfalseが含まれている場合、有効なパラメータ化が存在しません。 それ以外の場合、関数型 F<A'1、 ...、 A'm>の新しいパラメータ化は、≤ i ≤ mの場合、次のように構成されます。
Bにαi、 Tのインスタンス化(§18.1.3)が含まれている場合、A'i = Tになります。
それ以外の場合は、A'i = Aiです。
F<A'1、 ...、 A'm>が整形式型でない場合(つまり、型引数が境界内にない場合)、またはF<A'1、 ...、 A'm>がF<A1のサブタイプでない場合、Am>の場合、有効なパラメータ化は存在しません。 それ以外の場合、推測パラメータ化は、F<A'1、 ...、 A'm> (すべての型引数が型の場合)、または F<A'1、 ...、 A'm> (1つ以上の型引数がまだワイルドカードである場合)のワイルドカード以外のパラメータ化です。
ワイルドカード・パラメータ化された関数インタフェースの関数型を判別するには、ワイルドカード型の引数を特定の型で「インスタンス化」する必要があります。 「デフォルト」のアプローチは、§9.8で説明されているように、ワイルドカードをその境界に単に置き換えることです。ただし、ラムダ式にワイルドカード境界に対応しない明示的なパラメータ型がある場合、これは誤ったエラーを生成します。 たとえば:
Predicate<? super Integer> p = (Number n) -> n.equals(23);ラムダ式はPredicate<Number>で、これはPredicate<? super Integer>のサブタイプですが、Predicate<Integer>ではありません。 この項の分析は、NumberがPredicateの型引数に適切な選択であると推測するために使用されます。
とはいえ、ここでの分析は、一般的なタイプの推論に関して記述されているが、意図的に非常に単純である。 唯一の制約は等価制約です。つまり、削減は単純なパターン一致に相当します。 より強力な方法では、ラムダ式の本体から制約を推測することもできます。 ただし、周囲またはネストされた汎用メソッド呼出し(あるいはその両方)の推論との相互作用が考えられると、これは非常に複雑になります。
ある適用可能なメソッドが別のメソッドよりより具体的である(§15.12.2.5)ことをテストする場合、2番目のメソッドが汎用的であるときは、2番目のメソッドの型パラメータの一部のインスタンス化を推測して最初のメソッドを2番目のメソッドより具体的にできるかどうかをテストする必要があります。
m1を最初のメソッドとし、m2を2番目のメソッドにします。 m2には型パラメータP1、 ...、 Ppがあり、α1、 ...、 αpを推論変数とし、θを置換[P1:=α1、 ...、 Pp:=αp]とします。
e1、...、ekを、対応する呼出しの引数式にします。 次に、
m1およびm2が厳密な呼出しまたは緩い呼出し(§15.12.2.2、§15.12.2.3)によって適用される場合、S1、...、Skをm1の仮パラメータ・タイプにし、T1、...、Tkをm2の仮パラメータ・タイプに適用したθの結果にします。
m1およびm2が可変アリティ呼出し(§15.12.2.4)によって適用可能な場合は、S1、...、Skをm1の最初のk変数アリティ・パラメータ型とし、T1、...、Tkをm2の最初のk変数アリティ・パラメータ型に適用したθの結果にします。
S1、...、Skには置換が適用されないことに注意してください。m1が汎用の場合でも、m1の型パラメータは推論変数ではなく型変数として扱われます。
m1がm2より具体的かどうかを判断するプロセスは、次のとおりです。
まず、§18.1.3で指定されているように、P1、 ...、 Ppの宣言された境界から、初期境界セット Bが生成されます。
次に、すべての i (1 ≤ i ≤ k)について、制約式または制限のセットが生成されます。
Tiが適切な型の場合、SiがeiのTi(§15.12.2.5)より限定的な場合はtrueになり、それ以外の場合はfalseになります。 (Siは常に適切な型であることに注意してください。)
それ以外の場合は、Siと Tiが両方の関数型インタフェース型でない場合、制約式'Si <: Ti'が生成されます。
それ以外の場合、Siのインタフェースが Tiのインタフェースのスーパーインタフェースまたはサブインタフェースである場合(または、Siまたは Tiが交差タイプである場合)、Siの一部のインタフェースが Tiの一部のインタフェースのスーパーインタフェースまたはサブインタフェースである場合は、制約式'Si <: Ti'が生成されます。
それ以外の場合は、MTSをSiの取得のファンクション・タイプにし、MTSをSi(取得なし)のファンクション・タイプにし、MTTをTiのファンクション・タイプにします。 MTSとMTTの仮パラメータまたは型パラメータの数が異なる場合、またはMTSとMTSの型パラメータが同じでない場合(§8.4.4)、結果はfalseになります。 それ以外の場合は、MTSおよびMTTの型パラメータ、仮パラメータ型および戻り型から、次の制約式または境界が生成されます。
A1、 ...、 AnをMTSの型パラメータとし、B1、 ...、 BnをMTTの型パラメータにします。
θ'を置換[B1:=A1、 ...、 Bn:=An]とします。 次に、すべての j (1 ≤ j ≤ n)の場合:
Ajの境界がA1、...、Anのいずれかを示しており、Bjの境界が適切な型でない場合、falseになります。
それ以外の場合、XはAjの境界、YはBjの境界、X = Y θ'です。
バインドされた Ajが A1、...、Anのいずれかを示しており、Bjの境界が適切な型でない場合、等価制約を生成すると、範囲外の型変数によって推論変数がバインドされる可能性が高くなります。 範囲外の型変数を使用して推論変数をインスタンス化することは非意味的であるため、可能性が発生するたびにすぐにあきらめることで状況を回避することを好みます。 この単純化は完全性保存ではありません。 (同じコメントは、次の仮パラメータ型および戻り型の処理にも適用されます。)
U1、 ...、 UkをMTSの仮パラメータ型とし、V1、 ...、 VkをMTTの仮パラメータ型にします。 次に、すべての j (1 ≤ j ≤ k)の場合:
UjがA1、...、AnおよびVjのいずれかが適切な型でない場合、falseになります。
それ以外の場合、'Vj θ' <: Uj'およびU1'、 ...、 Uk'はMTSの仮パラメータ・タイプで、'A1'、 ...、 An'は、MTS'、 'Vj[B1:=A1'、 ...、 Bn:=An'] = Uj''の仮パラメータです。
RSをMTSの戻り型にし、RTをMTTの戻り型にします。 次に、
RSがA1、...、An、およびRTのいずれかが適切な型でない場合、falseになります。
それ以外の場合、eiが明示的に型指定されたラムダ式の場合:
RTがvoidの場合、true。
それ以外の場合、RSおよびRTがファンクション・インタフェース・タイプで、eiに少なくとも1つの結果式がある場合、eiの各結果式について、この2番目のステップ全体が推測制約に繰り返されます。この制約では、RSが指定された結果式のRTθより具体的です。
それ以外の場合、RSがプリミティブ型で、RTがそうでなく、eiに少なくとも1つの結果式があり、eiの各結果式がプリミティブ型trueのスタンドアロン式(§15.2)である場合。
それ以外の場合、RTがプリミティブ型で、RSがプリミティブ型でなく、eiに少なくとも1つの結果式があり、eiの各結果式が参照型のスタンドアロン式または多式trueのいずれかである場合。
それ以外の場合は、'RS <: RT θ''となります。
それ以外の場合、eiが正確なメソッド参照の場合:
RTがvoidの場合、true。
それ以外の場合、RSがプリミティブ型で、RTがプリミティブ型でなく、eiのコンパイル時宣言にプリミティブ戻り型trueがある場合。
それ以外の場合、RTがプリミティブ型で、RSがプリミティブ型でなく、eiのコンパイル時宣言に参照の戻り型trueがある場合。
それ以外の場合は、'RS <: RT θ''となります。
それ以外の場合、eiがカッコで囲まれた式の場合、RSおよびRTから導出された制約に対するこれらのルールは、含まれている式に対して再帰的に適用されます。
それ以外の場合、eiが条件式の場合、RSおよびRTから導出された制約に対するこれらのルールは、2番目と3番目の各オペランドに対して再帰的に適用されます。
それ以外の場合、eiがswitch式の場合、RSおよびRTから導出された制約に対するこれらのルールは、各結果式に対して再帰的に適用されます。
それ以外の場合は、falseです。
3つ目は、m2が可変アリティ呼出しによって適用され、k+1パラメータを持つ場合、Sk+1はm1のk+1'番目の可変アリティ・パラメータ・タイプで、Tk+1はk+1'番目の変数アリティ・パラメータ・タイプがm2の場合、制約'Sk+1 <: Tk+1'が生成されます。
第4に、生成された境界と制約の式が削減され、Bに組み込まれてバインドされたセットB'が生成されます。
B'にバインドされたfalseが含まれておらず、B'内のすべての推論変数の解決が成功した場合、m1はm2より限定的です。
それ以外の場合、m1はm2より限定的ではありません。
汎用レコード・クラスRのレコード・パターン(§14.30.1)が、T型の値と照合されるコンテキストに出現し、パターンがRの型引数を提供しない場合、次に説明するように型引数が推測されます。
Tがチェックされていない場合、変換可能(§5.5)をraw型 Rにキャストすると、推論は失敗します。
それ以外の場合、P1、 ...、 Pn (n ≥ 1)は Rの型パラメータであり、α1、 ...、 αnは推論変数です。 初期バインド・セットB0は、P1、 ...、 Pnの宣言された境界から生成されます(§18.1.3を参照)。
型 T'は、次のように Tから導出されます。
Tがパラメータ化された型である場合は、Tcを Tに適用された取得変換(§5.1.10)の結果にし、Z1、...、Zk (k ≥ 0)を Tcの型引数である取得によって生成される型変数にしてください。 (これには、このステップで取得変換によって生成される型変数と、取得変換によって生成される型変数が含まれます。) β1、 ...、 βk (k ≥ 0)を推論変数とし、θを置換[Z1:=β1、 ...、 Zk:=βk]とします。 T'は Tc θです。
β1、 ...、 βkの追加の境界は、次のようにB0に組み込まれ、バインドされたセットB1を形成します。
βi (1 ≤ i ≤ k)が型変数を上限 Uに置き換えた場合、境界 βi <: U θが境界セットに表示されます。
βi(1 ≤ i ≤ k)が型変数を下限Lに置き換えた場合、バインドされたL θ <: βiがバインド・セットに表示されます。
βi (1 ≤ i ≤ k)に適切な上限が存在しない場合、境界セットに βi <: Objectが表示されます。
Tがほかのクラスまたはインタフェースタイプである場合、T'は Tと同じで、B1は B0と同じです。
Tが型変数または交差型の場合は、交差型の型変数または要素の上限ごとに、このステップとステップ4が再帰的に繰り返されます。 ステップ3と4で作成したすべての範囲は、単一の範囲セットに組み込まれています。
T'が汎用クラスGのパラメータ化であり、R<α1、 ...、 αn>のスーパータイプが存在し、Gのパラメータ化でもある場合は、R'をそのスーパータイプにします。 制約式'T'=R''が削減され(§18.2)、結果の境界がB1に組み込まれて新しいバインド・セットB2が生成されます。
それ以外の場合、B2はB1と同じです。
B2にバインドされたfalseが含まれている場合、推論は失敗します。
それ以外の場合、推論変数 α1、 ...、 αnは B2 (§18.4)で解決されます。 通常の解決とは異なり、このケースの解決では、推論変数のインスタンス化を適切な下限または適切な上限から生成しようとするステップがスキップされます。かわりに、新しいインスタンス化が、新しい型変数を導入するステップに直接スキップして作成されます。
解決に失敗すると、推論は失敗します。
それ以外の場合は、A1、 ...、 Anを α1、 ...、 αnの解決済みのインスタンス化にし、Y1、 ...、 Yp (p ≥ 0)を解決によって導入された新しい型変数にしてください。
レコード・パターンのタイプは、Y1、 ...、 Yp (§4.10.5)に対するR<A1、 ...、 An>の上方投影です。
例18.5.5-1. レコード・パターンの型推論
次のプログラムは、レコード・パターンのパラメータ化を推測します。
import java.util.function.UnaryOperator;
record Mapper<T>(T in, T out) implements UnaryOperator<T> {
public T apply(T arg) {
return in.equals(arg) ? out : null;
}
}
class IllustrateRecordPatternTypeInference{
void test(UnaryOperator<? extends CharSequence> op) {
if (op instanceof Mapper(var in, var out)) {
boolean shorter = out.length() < in.length();
}
}
}
この場合、Rはレコード・クラスMapper、TはタイプUnaryOperator<? extends CharSequence>です。 Tは、生のMapperに変換可能なキャストがチェックされるため、Mapper<α>のαのインスタンス化を推測します。 T'はUnaryOperator<β>型で、βには上限CharSequenceがあります。
Mapper<α>にはスーパータイプUnaryOperator<α>があるため、制約式'UnaryOperator<β>= UnaryOperator<α>'を減らします。 これにより、範囲はα=βになります。 さらに組み込まれると、α <: CharSequenceになります。
ここで、αを解決し、α = Y (上限CharSequenceを持つ新しい型変数)を生成します。 最後に、レコード・パターンの型がMapper<? extends CharSequence>であると推測して、Yに対するMapper<Y>の上方投影を見つけます。
レコード・パターンのタイプがわかると、レコード・パターンのコンポーネント・パターンと照合されるコンポーネント・タイプを見つけることができます。 パターン変数inとoutはどちらもCharSequence型です。