BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース JavaのNestmateが進行中

JavaのNestmateが進行中

ブックマーク

原文(投稿日:2018/03/15)へのリンク

A note to our readers: You asked so we have developed a set of features that allow you to reduce the noise: you can get email and web notifications for topics you are interested in. Learn more about our new features.

オラクルがJEP 181 - "ネストベースのアクセス制御" - 俗に言う"nestmate"を発表した。これはプラットフォームへの技術的な拡張であり、Java 1.1で導入したアーキテクチャの20年分の負債を精算するものだ。

新機能はJavaのネストクラス (厳密ではないがたびたび"インナークラス"と言及される。ネストクラスの取りうる型の1つでしかないのに。) の実装につながっている。

一般的に、ネストした型が使われるのは2つの別々の目的のためだ。2つともカプセル化に関連している。

まず第一に、型はとても明確な理由で必要とされるだけなのかもしれない。それもコードの非常に小さな部分において。これが意味することは、実のところ実装詳細の一部であるのでしっかりとローカルに限定すべきであるということだ。

Javaの古いバージョンでは、これをする唯一の方法はネストした型だけだった。たとえばインタフェースの匿名実装といったものだ。実際にはJava 8になるとこのユースケースはラムダ式がかなり受け持つことになり、ローカルに閉じた型として匿名型を使うことは劇的に減った。とはいえ依然として使う場合もある。

代わりに、型はネストするかもしれない。他の型の内部への密接なアクセスがとりわけ必要だからだ。ネストした (たとえばメンバ) 型となることでメンバ変数やメソッドと同じ方法でアクセスする。これが意味することはネストした型は特権アクセスし、"カプセル化のルールをやや曲げる"ものとして捉えられるということだ。

このネストした型のユースケースについて考えられる他の方法は、何らかの形で他の型に結びついた型であることだ。これが意味することは実際にはエンティティとして完全に独立しておらず、他の型と共存して生きているだけだということだ。

nestmateのJEPの目的は型のこの共生関係を一般化し形式化し、一定量の技術的負債を持っており現代の視点ではかなり洗練されていない現在の実装を整理することである。

Java 10の時点でもネストクラスは別々のトップレベルのクラスファイルにコンパイルされるが、特別な命名規則があり、クラスOuterにあるネストクラスNestedOuter$Nested.classという名前のファイルにコンパイルされる。

この実装方針にある問題は、Java言語規則によるとネストクラスはエンクロージングクラスの、privateメンバを含むすべてのメンバにアクセスできるということである。

この問題の解決のため、javacはアクセスできるようOuterに合成アクセサメソッドを追加する。たとえばこの単純なインナークラスを考えてみよう。

public class Outer {
    private int i = 0;
    
    public class Inner {
        public int i() {
            return i;
        }
    }
}

これは2つのクラスファイルOuter.classOuter$Inner.classとなり、バイトコードはこうなる。

public class Outer {
  private int i;

  public Outer();
    Code:
       0: aload_0
       1: invokespecial #2                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: iconst_0
       6: putfield      #1                  // Field i:I
       9: return

  static int access$000(Outer);
    Code:
       0: aload_0
       1: getfield      #1                  // Field i:I
       4: ireturn
}

そしてこうだ。

public class Outer$Inner {
  final Outer this$0;

  public Outer$Inner(Outer);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:LOuter;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return

  public int i();
    Code:
       0: aload_0
       1: getfield      #1                  // Field this$0:LOuter;
       4: invokestatic  #3                  // Method Outer.access$000:(LOuter;)I
       7: ireturn
}

インナークラスが必要とするprivateへのアクセスはコンパイラによってOuterのパッケージprivateなアクセサメソッドaccess$000()に変換されている。この合成アクセサの存在が意味することはメカニズムを知る開発者は直接またはリフレクション経由のどちらでもそこにアクセスできるということだ - たとえそれらが元々のクラスのnestmateでなくてもできる。

Javaのロードマップの一部としてアクセス制御を整理するために取られたステップで、ネストした型のこの側面は整理を必要としているものとして突出している。JEPではその動機を次のように述べている。

ネストを形成するクラスファイルのグループへの正式な理解では、nestmateも共通のアクセス制御メカニズムを使用することになるのですが、それにより望まれる結果をより単純で安全、透過的な方法で達成できます。

JEPの記述では今後の拡張が以下のことを含むとも述べている。

  • ジェネリックの特化において、特化したそれぞれの型はジェネリック型のnestmateとして生成される。
  • 安全でサポートされたUnsafe.defineAnonymousClass() APIの代替は既存クラスのnestmateとなる新しいクラスを作成するだろう。
  • "sealedクラス"のコンセプトはnestmateである許可されたサブクラスだけで達成されるだろう。
  • 真にプライベートなネストした型が達成されるだろう (現在プライベートなネストした型はパッケージアクセスで定義されている) 。

sealedクラスと真にプライベートな型はScalaやそれらをサポートする他言語を使っている開発者に人気が出る可能性が高い。そうした言語はパターンマッチングといった先進的機能において役立つ代数データ型のようなアイデアの実装に必要なビルディングブロックを提供しているからだ。

nestmateはProject Valhallaの一部として開発されてきた。実用レベルの初期プロトタイプ作成作業が本格化しており、OpenJDK開発者により活発に取り組まれている。

長期の機能によくあることだが、オラクルはnestmateがいつ (仮でさえ) リリースされるかに関して一切約束はしていない。しかし、現在開発中の重要な他の機能 (sealedクラスとパターンマッチング) への有用性を考慮すると、興味を持っているJavaプログラマはプロジェクトの経過を見張っておくべきだろう。

 
 

Rate this Article

Adoption Stage
Style
 
 

この記事に星をつける

おすすめ度
スタイル

BT