私は新しい Eclipse Neon で作業しており、コードの一部ですぐにエラーが発生し始めました。
これは最初は奇妙に思えましたが、ここで Neon ECJ (Eclipse Java Compiler) が JDK 9 初期リリース コンパイラの姿勢を採用していることに気付きました。
そのリンクにあるのと同じ問題は発生しませんが、ここで説明する別の問題が発生します。
フィールドとしてのラムダ式宣言に関する問題
これは、Eclipse Neon、JDK 9コンパイラ、および JDK 8 コンパイラ (Eclipse の以前のバージョンではありません) でコンパイル エラーが発生するテスト クラスです。
public class Weird
{
private final Function<String, String> addSuffix =
text -> String.format( "%s.%s", text, this.suffix );
private final String suffix;
public Weird( String suffix )
{
this.suffix = suffix;
}
}
上記のコードの場合、4 行目のエラーは次のsuffix
とおりです。
╔══════════╦═══════════════════════════════════════════════╗
║ Compiler ║ Error ║
╠══════════╬═══════════════════════════════════════════════╣
║ ECJ ║ Cannot reference a field before it is defined ║
║ JDK 9 ║ error: illegal forward reference ║
╚══════════╩═══════════════════════════════════════════════╝
宣言の前にsuffix
フィールド宣言を移動すると、同じクラスで何が起こるかを見てみましょう。
addSuffix
public class Weird
{
private final String suffix;
private final Function<String, String> addSuffix =
text -> String.format( "%s.%s", text, this.suffix );
public Weird( String suffix )
{
this.suffix = suffix;
}
}
上記のコードの場合、6 行目のエラーは次のsuffix
とおりです。
╔══════════╦════════════════════════════════════════════════════════════╗
║ Compiler ║ Error ║
╠══════════╬════════════════════════════════════════════════════════════╣
║ ECJ ║ The blank final field suffix may not have been initialized ║
║ JDK 9 ║ error: variable suffix might not have been initialized ║
╚══════════╩════════════════════════════════════════════════════════════╝
Java 9 はこのように動作する必要がありますか?
これは JDK 8 では問題なく機能しました。突然強制するのは奇妙なことのようです。特に、最終フィールドが正しくインスタンス化されていることを確認するためのコンパイル時のチェックが既に行われていることを考慮してください。
したがって、関数addSuffix
がアクセスされるまでに、値が所定の位置にある必要がありますsuffix
(null
そうでない場合は別の話です)。
また、JDK9 および ECJ で正常にコンパイルされる次のコードを試したことにも注意してください。
public class Weird
{
private final String suffix;
private final Function<String, String> addSuffix =
new Function<String, String>()
{
@Override
public String apply( String text )
{
return String.format( "%s.%s", text, suffix );
}
};
public Weird( String suffix )
{
this.suffix = suffix;
}
}
JDK 9 では、匿名クラス宣言と Lambda 式の間に大きな違いがあるようです。したがって、コンパイラ エラーが発生した場合、少なくとも ECJ は JDK 9 コンパイラを正確に模倣しています。
ストリームとジェネリックの問題
コード内の Generic が示すものとは異なる方法でコンパイラがこれを解釈する理由が思いつかないため、これには本当に驚きました。
public class Weird
{
public void makePDFnames( String [] names )
{
final List<String> messages = Arrays.asList( "nice_beard", "bro_ski" );
final List<String> components = messages.stream()
.flatMap( s -> Stream.of( s.split( "_" ) ) )
.collect( Collectors.toList() );
}
}
このコードでは、次のエラーが発生します。
╔══════════╦═══════════════════════════════════════════════════════════════════════╗
║ Compiler ║ Error ║
╠══════════╬═══════════════════════════════════════════════════════════════════════╣
║ ECJ ║ Type mismatch: cannot convert from List<Serializable> to List<String> ║
║ JDK 9 ║ NO ERROR. Compiles fine! ║
╚══════════╩═══════════════════════════════════════════════════════════════════════╝
この情報に照らして、この場合、JDK 9 を適切に模倣していないことは ECJ に問題があり、単なる Eclipse のバグであることがわかります。