Java
Amber

Amberで検討されているJava構文の変更

Amberとは

Java言語を拡張するプロジェクトです
http://openjdk.java.net/projects/amber/

Amberのブランチ

http://hg.openjdk.java.net/amber/amber/branches

  • datum データクラス
  • patterns パターンマッチ
  • switch 拡張switch
  • raw-string-leterals 文字列リテラルの拡張
  • lambda-leftovers ラムダの微修正
  • enhanced-enums 拡張enum
  • lvti ローカル変数型推論

これが、JDK10、11、12...と半年ごとに五月雨式にリリースされていくことになると思います。結構恐怖ですね。

データクラス

データ保持用のクラスです。
JEPは今のところ見当たりません。
http://cr.openjdk.java.net/~briangoetz/amber/datum.html
http://hg.openjdk.java.net/amber/amber/file/15a61b5af8f5/test/langtools/tools/javac/datum

recordとして定義します。

record Foo(int x, int y) {}

継承できるのはAbstract recordのみ

abstract record Sup(int x, int y) {}
record Bar(int x, int y, int z) extends Sup(x, y);

スーパーrecordに渡すのは同じ名前でないといけない

明示的にコンストラクタを定義する場合の値の設定はdefaultを使う

record Foo(int x, int y) {
  Foo(int x, int y) {
    default(x, y);
  }
}

ガードとして条件を指定できる

record Range1(int lo, int hi) where lo <= hi;
record Range2(int lo, int hi) where lo <= hi {};

sealedが指定できるっぽいけど、まだテストがなさそう。datumとpatternにまたがってるので実装は まだかな

パターンマッチング

パターンマッチングです。
http://openjdk.java.net/jeps/305

値 matches パターンで、値をマッチさせることができます。
パターンは、定数か変数定義です。変数定義の場合には、型が一致していた場合にtrueになりその変数に値が割り当てられます。

if (x matches Integer i) {
    // can use i here
}

switchでもパターンマッチが使えます。

String formatted;
switch (obj) {
    case Integer i: formatted = String.format("int %d", i); break;
    case Byte b:    formatted = String.format("byte %d", b); break;
    case Long l:    formatted = String.format("long %d", l); break;
    case Double d:  formatted = String.format("double %f", d); break;
    case String s:  formatted = String.format("String %s", s); break
    default:        formatted = obj.toString();
}

つまり、switchでdoubleやbooleanも使えるようになるということです。

switch (obj) {
  case 12 :   msg = "12だ"; break;
  case true : msg = "とぅるー"; break;
  case 3.14:  msg = "円周率では"; break;
}

JEP 305には含まれませんが、データクラスと組み合わせて構造の分解を行うことができるようになるということも目標です。

record Point(int x, int y) {}

int getLen(Point p) {
  return switch (p) {
    case Point(0, int y) -> y;
    case Point(int x, 0) -> x;
    case Point(int x, int y) -> (int)sqrt(x * x + y * y);
  }
}

拡張switch

http://openjdk.java.net/jeps/325

switchはステートメントでしたが、多くのswitchで同一の変数に値を割り当てたりすべてのcaseでreturnしたりといった使いかががされていたため、式としても使えるようになります。

つまり、こう。

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY -> 7;
    case THURSDAY, SATURDAY -> 8;
    case WEDNESDAY -> 9;
};

breakで値を返すということもできます。

int result = switch (s) {
    case "Foo":
        break 1;
    case "Bar":
        break 2;
    default:
        System.out.println("Neither Foo nor Bar, hmmm...");
        break 3;
}

基本的な形はbreakで値を返すものです。

case LABEL: break expression;

そのシンタックスシュガーとしてラムダっぽく書けるということのようです。

case LABEL -> expression;

casenullも使えるようになります。

String formatted = switch (s) {
    case null -> "(null)";
    case "" -> "(empty)";
    default -> s;
}

case nullがない場合には次のようなcaseが自動的に挿入されます。

case null: throw new NullPointerException();

また、caseに複数の値を指定できるようにもなります。

switch (day) {
    case MONDAY, FRIDAY, SUNDAY: 
        numLetters = 6;
        break;
    ...
};

このようなcaseの拡張は、既存のswitchステートメントでも有効です。

文字列リテラルの拡張

http://openjdk.java.net/jeps/326

改行などを含んだ文字列を定義できます。バッククオートで囲みます。

var str1 = `string literal is "test".`
var str2 = ``raw string literal is `test`.``
var str3 = `You can write
            two line string.` // same as "You can write\ntwo line string."

"""で囲む形式も提案されてたけど、バッククオートに一本化された模様。
Qiitaが```のようにバッククオートをバッククオートで囲めないので困る人が続出する気がする。
2018/5/8時点では次のようにレンダリングされている。
スクリーンショット 2018-05-08 4.38.25.png

ラムダの微修正

ラムダの微修正
http://openjdk.java.net/jeps/302

使わない変数に_を使えます。

BiFunction<Integer, String, String> biss = (i, _) -> String.valueOf(i);

あと、次のような場合、falseを返すのでPredicateになることは明確ですが、現状ではエラーになってます。これをちゃんと型推論できるようにしようというもの。

m(Predicate<String> ps) { ... }
m(Function<String, String> fss) { ... }

m(s -> false) //ambiguous

拡張enum

enumでgenericsを指定できるようにしようというものです。
http://openjdk.java.net/jeps/301

enum Argument<X> { // declares generic enum
   STRING<String>(String.class), 
   INTEGER<Integer>(Integer.class), ... ;

   Class<X> clazz;

   Argument(Class<X> clazz) { this.clazz = clazz; }

   Class<X> getClazz() { return clazz; }
}

Class<String> cs = Argument.STRING.getClazz(); //uses sharper typing of enum constant

ローカル変数型推論

ローカル変数の型推論を導入するものです。
http://openjdk.java.net/jeps/286

var x = 12;

JDK 10で導入されることが決まっています。

ラムダへのvar対応

ラムダの変数定義にもvarを明示的に書けるようにしようというものです。
http://openjdk.java.net/jeps/323

いままでこう書けていました。

(x, y) -> x + y

varで明示的に型推論であることを指定しようというものです。

(var x, var y) -> x + y

混在はできません

(var x, y) -> x + y

JDK 11に入りそう。

構文以外

condyという名前で、indyのコンストラクタ版が開発されているぽい。