Project Amberの概要

*1. Project Amber *とは

  • https://openjdk.java.net/projects/amber/ [Project Amber]は、JavaとOpenJDKの開発者による現在のイニシアチブであり、開発プロセスをより良くするためにJDKにいくつかの小さなしかし重要な変更を提供することを目指しています。 これは2017年から継続しており、すでにJava 10および11にいくつかの変更を提供しており、他の変更はJava 12に含める予定であり、今後のリリースでさらに追加される予定です。

    これらの更新はすべてhttps://en.wikipedia.org/wiki/JDK_Enhancement_Proposal[JEP] – JDK Enhancement Proposalスキームの形式でパッケージ化されています。

2. 配信されたアップデート

これまで、Project Amberは、現在リリースされているJDKのバージョン(https://openjdk.java.net/jeps/286[JEP-286]およびhttps://openjdk.java.net/jeps)にいくつかの変更を提供しました。 / 323 [JEP-323]。

* 2.1。 ローカル変数型推論*

  • Java 7は、ジェネリックを扱いやすくする方法としてDiamond Operatorを導入しました*。 この機能は、変数を定義するときに、同じステートメントで一般的な情報を複数回記述する必要がなくなったことを意味します。

List<String> strings = new ArrayList<String>(); // Java 6
List<String> strings = new ArrayList<>(); // Java 7
  • Java 10にはJEP-286の完成した作業が含まれていたため、Javaコードは、コンパイラーが既に使用可能な場所で型情報を複製する必要なく、ローカル変数を定義できます。 これは、より広いコミュニティで_var_キーワードと呼ばれ、他の多くの言語で利用できるのと同様の機能をJavaにもたらします。

    この作業により、*ローカル変数を定義するときはいつでも、完全な型定義の代わりに_var_キーワードを使用できます*。コンパイラは、使用する正しい型情報を自動的に算出します。
var strings = new ArrayList<String>();
*上記では、変数_strings_は_ArrayList <String>()_ *型であると決定されますが、同じ行に情報を複製する必要はありません。
*値の決定方法に関係なく、ローカル変数を使用する場所であればどこでも使用できます*。 これには、戻り値の型と式、および上記のような単純な割り当てが含まれます。
word _var_は、予約語ではないという特殊なケースです。 代わりに、特別な型名です。 これは、変数名を含むコードの他の部分にこの単語を使用できることを意味します。 混乱を避けるため、これを行わないことを強くお勧めします。
*ローカル型推論は、宣言の一部として実際の型を提供する場合にのみ使用できます*。 値が明示的に_null、_である場合、値がまったく提供されていない場合、または提供された値が正確なタイプを判別できない場合(Lambda定義など)、機能しないように意図的に設計されています。
var unknownType; // No value provided to infer type from
var nullType = null; // Explicit value provided but it's null
var lambdaType = () -> System.out.println("Lambda"); // Lambda without defining the interface
ただし、呼び出し自体が型情報を提供するため、他の呼び出しからの戻り値である場合、値は_null_になる可能性があります。
Optional<String> name = Optional.empty();
var nullName = name.orElse(null);
この場合、_nullName_は_String_型を推測します。because_name.orElse()_の戻り値の型がそれだからです。
*この方法で定義された変数は、他の変数*と同じ方法で他の修飾子を持つことができます*-例えば、_transitive、synchronized、_、_final_。

* 2.2。 ラムダのローカル変数型推論*

上記の作業により、型情報を複製する必要なくローカル変数を宣言できます。 ただし、これはパラメーターリストでは機能せず、特に、ラムダ関数のパラメーターでは機能しません。
Java 10では、Lambda関数を次の2つの方法のいずれかで定義できます。明示的に型を宣言するか、完全に省略することにより:
names.stream()
  .filter(String name -> name.length() > 5)
  .map(name -> name.toUpperCase());
ここで、2行目には明示的な型宣言– _String_ –がありますが、3行目では完全に省略されており、コンパイラーは正しい型を見つけ出します。 *ここでできないことは、_var_型を使用することです*。
  • Java 11ではこれを実現できます*。そのため、代わりに次のように記述できます。

names.stream()
  .filter(var name -> name.length() > 5)
  .map(var name -> name.toUpperCase());
*これは、コードの他の場所での_var_型の使用と一貫しています*。
ラムダは常に、すべてのパラメーターに完全な型名を使用するか、いずれにも使用しないように制限しています。 これは変更されておらず、* _var_の使用はすべてのパラメーターで使用する必要があります。
numbers.stream()
    .reduce(0, (var a, var b) -> a + b); // Valid

numbers.stream()
    .reduce(0, (var a, b) -> a + b); // Invalid

numbers.stream()
    .reduce(0, (var a, int b) -> a + b); // Invalid
ここで、2つのラムダパラメータが両方とも_var_を使用しているため、最初の例は完全に有効です。 *ただし、3番目のケースでは明示的な型名もありますが、2番目と3番目のパラメータは1つのパラメータのみが使用するため_ * var * _です。

3. 差し迫ったアップデート

リリースされたJDKですでに利用可能な更新に加えて、次のJDK 12リリースには1つの更新が含まれています– https://openjdk.java.net/jeps/325[JEP-325]。

* 3.1。 式の切り替え*

  • JEP-325は、_switch_ステートメントの動作方法を単純化し、それらを式として使用できるようにする*サポートを提供し、それらを使用するコードをさらに単純化します。

    現在、_switch_ステートメントはCやCなどの言語のステートメントと非常によく似た方法で機能します。 *これらの変更により、Kotlinの_when_ステートメントまたはScala *の_match_ステートメントにより類似したものになりました。
    これらの変更により、switchステートメントを定義するための構文は、_-> _シンボルを使用して、ラムダの構文に似ています。 これは、大文字と小文字の区別と実行されるコードの間にあります。
switch (month) {
    case FEBRUARY -> System.out.println(28);
    case APRIL -> System.out.println(30);
    case JUNE -> System.out.println(30);
    case SEPTEMBER -> System.out.println(30);
    case NOVEMBER -> System.out.println(30);
    default -> System.out.println(31);
}
  • _break_キーワードは必要ないことに注意してください。さらに、ここでは使用できません*。 すべての一致は明確であり、フォールスルーはオプションではないことが自動的に暗示されます。 代わりに、必要に応じて古いスタイルを引き続き使用できます。

    *矢印の右側は、式、ブロック、またはthrowsステートメントのいずれかでなければなりません*。 それ以外はエラーです。 これにより、ブロック内でのみ発生するswitchステートメント内で変数を定義する問題も解決されます。つまり、変数はそのブロックに自動的にスコープされます。
switch (month) {
    case FEBRUARY -> {
        int days = 28;
    }
    case APRIL -> {
        int days = 30;
    }
    ....
}
**古いスタイルのswitchステートメントでは、変数が重複しているため、これはエラーになります** __ * days *。 __ブロックを使用する必要があるため、これは回避されます。
*矢印の左側には、コンマで区切られた任意の数の値を指定できます。*これは、フォールスルーと同じ機能の一部を許可しますが、一致全体に対してのみ使用でき、偶然には使用できません。
switch (month) {
    case FEBRUARY -> System.out.println(28);
    case APRIL, JUNE, SEPTEMBER, NOVEMBER -> System.out.println(30);
    default -> System.out.println(31);
}
これまでのところ、this_switch_ステートメントが機能し、より簡潔にする現在の方法で、これらすべてが可能です。 ただし、*この更新により、_switch_ステートメントを式として使用する機能も提供されます*。 これはJavaにとって重要な変更ですが、他のJVM言語(他のJVM言語を含む)が機能し始めている数と一致しています。
*これにより、_switch_式が値に解決され、その値を他のステートメントで使用できるようになります*-たとえば、割り当て:
final var days = switch (month) {
    case FEBRUARY -> 28;
    case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30;
    default -> 31;
}
ここでは、_switch_式を使用して数値を生成し、その数値を変数に直接割り当てています。
*以前は、変数_days_を_null_として定義し、_switch_ケース内の値を割り当てることによってのみ可能でした*。 つまり、_days_は最終的なものではなく、ケースを見逃した場合は未割り当てになる可能性があります。

4. 今後の変更

これまでのところ、これらの変更はすべて既に利用可能であるか、今後のリリースに含まれます。 * Project Amberの一部として提案されている変更のうち、まだリリースが予定されていないものがあります。*

* 4.1。 生の文字列リテラル*

*現時点では、Javaには文字列リテラルを定義する方法が1つだけあります。コンテンツを二重引用符で囲んでいます*。 これは使いやすいですが、より複雑な場合には問題が発生します。
具体的には、*特定の文字を含む文字列を書くことは困難です*-改行、二重引用符、バックスラッシュ文字を含むがこれらに限定されません。 これは、これらの文字が通常よりも一般的である可能性があるファイルパスおよび正規表現で特に問題になる可能性があります。
  • https://openjdk.java.net/jeps/326 [JEP-326]は、Raw String Literals *と呼ばれる新しい文字列リテラルタイプを導入します。 これらは二重引用符の代わりにバックティックマークで囲まれ、その中に任意の文字を含めることができます。

    *これは、複数行にわたる文字列と、引用符またはバックスラッシュを含む文字列をエスケープすることなく書き込むことができるようになることを意味します。したがって、読みやすくなります。
    例えば:
// File system path
"C:\\Dev\\file.txt"
`C:\Dev\file.txt`

// Regex
"\\d+\\.\\d\\d"
`\d+\.\d\d`

// Multi-Line
"Hello\nWorld"
`Hello
World`
3つのケースすべてで、*バックティック付きのバージョンで何が起こっているかを簡単に確認できます。これにより、タイプミスが発生しにくくなります*。
*新しいRaw String Literalsにより、バックティック自体を複雑にすることなく含めることができます*。 文字列の開始と終了に使用されるバックティックの数は、必要なだけ長くできます。バックティックは1つだけである必要はありません。 文字列は、等しい長さのバックティックに到達したときにのみ終了します。 だから、例えば:
``This string allows a single "`" because it's wrapped in two backticks``
これにより、特定の文字を機能させるために特別なシーケンスを必要とするのではなく、文字列をそのまま入力することができます。

* 4.2。 ラムダの残り物*

https://openjdk.java.net/jeps/302[JEP-302]は、ラムダの動作方法にいくつかの小さな改善を導入します。
主な変更点は、パラメーターの処理方法です。 まず、*この変更により、不要な名前にアンダースコアを使用できるようになり、不要な名前が生成されなくなりました*。 これは以前は可能でしたが、アンダースコアが有効な名前だったため、単一のパラメーターに対してのみでした。
Java 8では、名前にアンダースコアを使用すると警告になるように変更が加えられました。 Java 9はこれを進行させ、代わりにエラーになり、まったく使用できなくなりました。 この今後の変更により、競合を引き起こすことなくラムダパラメータを使用できるようになります。 これにより、たとえば次のコードが許可されます。
jdbcTemplate.queryForObject("SELECT * FROM users WHERE user_id = 1", (rs, _) -> parseUser(rs))
この機能強化では、* 2つのパラメーターでラムダを定義しましたが、最初のパラメーターのみが名前にバインドされています*。 2番目はアクセスできませんが、同様に、使用する必要がないため、この方法で記述しました。
*この拡張機能のその他の主要な変更は、ラムダパラメーターが現在のコンテキストから名前をシャドウできるようにすることです*。 これは現在許可されていないため、理想的ではないコードを記述する可能性があります。 例えば:
String key = computeSomeKey();
map.computeIfAbsent(key, key2 -> key2.length());
*コンパイラ以外に、__key __and _key2_が名前を共有できない理由はありません*。 ラムダは変数_key_を参照する必要がなく、これを強制することでコードが面倒になります。
代わりに、この機能強化により、より明確で簡単な方法で記述できます。
String key = computeSomeKey();
map.computeIfAbsent(key, key -> key.length());
さらに、*この拡張機能には、オーバーロードメソッドにラムダ引数がある場合にオーバーロード解決に影響する可能性のある変更が提案されています*。 現在、オーバーロード解決が機能するルールのためにこれがあいまいさを引き起こす場合があり、このJEPはこのあいまいさの一部を回避するためにこれらのルールをわずかに調整する場合があります。
たとえば、*現在、コンパイラは次のメソッドをあいまいであると見なします*。
m(Predicate<String> ps) { ... }
m(Function<String, String> fss) { ... }
これらのメソッドは両方とも、単一の_String_パラメーターを持ち、非void戻り値型を持つラムダを取ります。 *開発者には、両者が異なることは明らかです。1つは_String_を返し、もう1つは_boolean_を返しますが、コンパイラはこれらをあいまいとして処理します*。
このJEPはこの欠点に対処し、このオーバーロードを明示的に処理できるようにします。

* 4.3。 パターンマッチング*

  • https://openjdk.java.net/jeps/305 [JEP-305]では、_instanceof_演算子と自動型強制を使用して作業する方法が改善されています。*

    現在、Javaで型を比較す​​る場合、_instanceof_演算子を使用して値が正しい型であるかどうかを確認する必要があります。その後、値を正しい型にキャストする必要があります。
if (obj instanceof String) {
    String s = (String) obj;
    // use s
}
これは機能し、すぐに理解されますが、必要以上に複雑です。 *コードには非常に明白な繰り返しがいくつかあるため、エラーが忍び込む可能性があります。*
*この拡張機能は、以前にJava 7の_try-with-resources_で行われていたように、_instanceof_演算子に対して同様の調整を行います。 この変更により、比較、キャスト、および変数宣言は代わりに単一のステートメントになります。
if (obj instanceof String s) {
    // use s
}
*これにより、重複がなく、エラーが忍び寄るリスクのない*単一のステートメントが得られますが、上記と同じように実行されます。
これはブランチ間でも正常に機能し、以下が機能します。
if (obj instanceof String s) {
    // can use s here
} else {
    // can't use s here
}
*拡張機能は、必要に応じてさまざまなスコープの境界を越えて正しく機能します*。 _instanceof_句で宣言された変数は、予想どおり、変数の外側で定義された変数を正しくシャドウします。 ただし、これは適切なブロックでのみ発生します。
String s = "Hello";
if (obj instanceof String s) {
    // s refers to obj
} else {
    // s refers to the variable defined before the if statement
}
*これは、同じ_if_句内でも機能します。これは、_null_チェックに依存するのと同じ方法です。
if (obj instanceof String s && s.length() > 5) {
    // s is a String of greater than 5 characters
}
*現在、これは_if_ステートメントに対してのみ計画されています*が、将来の作業ではおそらく_switch式_でも動作するように拡張されるでしょう。

* 4.4。 簡潔なメソッドボディ*

https://openjdk.java.net/jeps/8209434[*JEP Draft 8209434 *] *ラムダ定義の仕組みと同様の方法で、簡略化されたメソッド定義をサポートするための提案*です。
*今、3つの異なる方法でLambdaを定義できます*:ボディ、単一の式、またはメソッド参照として:
ToIntFunction<String> lenFn = (String s) -> { return s.length(); };
ToIntFunction<String> lenFn = (String s) -> s.length();
ToIntFunction<String> lenFn = String::length;
ただし、*実際のクラスメソッド本体の記述に関しては、現時点では完全に記述しなければなりません*。
*この提案は、該当する場合に、これらのメソッドの式およびメソッド参照フォームもサポートすることです*。 これにより、特定のメソッドを現在よりもずっとシンプルに保つことができます。
たとえば、ゲッターメソッドは完全なメソッド本体を必要としませんが、単一の式に置き換えることができます。
String getName() -> name;
同様に、他のメソッドの単なるラッパーであるメソッドを、パラメータの受け渡しを含むメソッド参照呼び出しで置き換えることができます。
int length(String s) = String::length
*これらは、理にかなっている場合に、より簡単なメソッドを許可します*。つまり、クラスの残りの部分で実際のビジネスロジックを不明瞭にする可能性が低くなります。
これはまだドラフト状態であり、そのため配信前に大幅な変更が行われる可能性があることに注意してください。

5. 強化された列挙

https://openjdk.java.net/jeps/301[JEP-301]は、以前Project Amberの一部になる予定でした。 *これにより、列挙型にいくつかの改善がもたらされ、個々の列挙型要素に個別のジェネリック型情報を明示的に許可します*
たとえば、次のことが許可されます。
enum Primitive<X> {
    INT<Integer>(Integer.class, 0) {
       int mod(int x, int y) { return x % y; }
       int add(int x, int y) { return x + y; }
    },
    FLOAT<Float>(Float.class, 0f)  {
       long add(long x, long y) { return x + y; }
    }, ... ;

    final Class<X> boxClass;
    final X defaultValue;

    Primitive(Class<X> boxClass, X defaultValue) {
       this.boxClass = boxClass;
       this.defaultValue = defaultValue;
    }
}
残念ながら、* Javaコンパイラアプリケーション内でのこの機能強化の実験により、以前考えられていたよりも実行性が低いことが証明されました*。 列挙型要素にジェネリック型情報を追加すると、それらの列挙型を他のクラス(たとえば、_EnumSet_)のジェネリック型として使用できなくなりました。 これにより、拡張の有用性が大幅に低下します。
したがって、*これらの詳細が解決されるまで、*この機能強化は現在保留中です*。

6. 概要

ここでは、さまざまな機能について説明しました。 それらのいくつかはすでに利用可能ですが、他のものはすぐに利用可能になりますが、さらに多くは将来のリリースで計画されています。 これらは現在および将来のプロジェクトをどのように改善できますか?