2016-12-16 6 views
12

私はfollwing Javaの9モジュールがあります。内部型をリークするパブリックAPIのコンパイルが失敗しないのはなぜですか?

public class Api { 

    public static void foo(ImplDetail args) {} 
} 

と非エクスポートタイプ:エクスポートされたタイプでは

module com.example.a { 
    exports com.example.a; 
} 

package com.example.b.internal; 

public class ImplDetail {} 

エクスポートタイプが非を使用していますパブリックメソッドのメソッドパラメータ型としてエクスポートされた型。他のモジュールのクライアントは、パラメータタイプをインスタンス化できないため、foo()メソッドを実際に呼び出せなかったので、コンパイラはこのような一貫性のないクラス構成を拒否すると仮定していました。

私の驚いたことに、このモジュールはjavacによって正常にコンパイルされています。私はnullを渡す特別なケースを見ることができますが、私はそのようなAPI定義が不正なものだと考えていて、理想的にコンパイラによって強制されてはならないと考えています。

このようなケースを許可しない理由は何ですか?

答えて

8

APIでエクスポートされていない型を使用すると、スタイルが悪くなり、設計上のエラーになる可能性がありますが、javacがこれをコンパイル時エラーにする位置にないことは明らかです。

パブリックAPIでプライベートタイプを使用することは、常にJava 1.0に戻ってきていることに注意してください。

モジュール外のコードではまだApi.foo(null)を呼び出すことができます。

呼び出し元がnull以外の参照でこのAPIを引き続き使用できる場合があります。パッケージcom.example.aのクラスpublic class Sub extends ImplDetailを考えてみましょう。このクラスSubは公開されており、エクスポートされているため、モジュールの外部でコード化することができます。したがって、外部コードは、Subのインスタンスをどこかから取得してApi.foo(sub)を呼び出すことができます。

しかし、確かにjavacはエクスポートされたパッケージにサブタイプがImplDetailであるかどうかを確認でき、存在しない場合はコンパイル時エラーが発生します。必ずしも。別々のコンパイルの可能性のため、新しいクラスは、Apiを含むコンパイルステップの後にモジュールに導入される可能性があります。つまり、module-info.classファイルを再コンパイルして、エクスポートされたパッケージのセットを変更することができます。

私は、javacがApiクラスをコンパイルするときにエラーを発生させることは不適切だと思います。 Javacには、このような場合に警告として警告するオプション-Xlint:exportsがあります。

jmodツールや事後モジュール監査ツールなどのビルドプロセスの後に、エクスポートされたAPIで使用されているエクスポートされていないタイプの使用にもフラグが立てられます。私は現在何もこれをしているとは思わない。

+0

あなたの詳細な回答ありがとう、スチュアート!輸出されていない型から輸出された型を派生させることは、私にとっても一貫していないように思えるので、(Sub'自体のために)コンパイルエラーを保証する必要があると言いました。しかし、おそらく、いくつかのコンパイル・ステップで記述した同じ問題があるでしょう。 – Gunnar

+0

@ Gunnarこれをしばらく考えた後、プライベートクラスのサブクラスであるパブリッククラスを持つことは珍しいことではないことに気付きました。たとえば、java.langのJDKでは、 'StringBuffer'と' StringBuilder'はどちらもプライベートクラスである 'AbstractStringBuilder'のパブリックサブクラスです。内部プライベートクラスを持つ階層SB <:ASB <:Objectを持つのはちょっと奇妙ですが、実装を共有するためには完了です。ただし、ASBはAPIの型として公開されません。 –

+0

フォローアップありがとう。個人的に、私はそのようなデザインが厄介であると感じます。タイプのユーザは、タイプIMOの完全な階層を見ることができるはずです。実装の共有については、プライベート共有クラスへの委任によって行うことができます。 – Gunnar

関連する問題