13

次の2つのJavaクラスについて考えてみます。

a.) class Test { void foo(Object foobar) { } }

b.) class Test { void foo(pkg.not.in.classpath.FooBar foobar) { } }

pkg.not.in.classpath.FooBarさらに、それがクラスパスに見つからないと仮定します。

最初のクラスは、標準のjavacを使用して正常にコンパイルされます。

ただし、2番目のクラスはコンパイルされず、javacはエラーメッセージを表示します"package pkg.not.in.classpath does not exist"

依存関係をチェックすると、メソッドの引数が間違っているかどうかなどをコンパイラが通知できるため、エラーメッセージは一般的な場合に適しています。

コンパイル時の依存関係のこのチェックは便利で便利ですが、上記の例でJavaクラスファイルを生成するために厳密に必要なわけではありません。

  1. コンパイル時の依存関係チェックを実行せずに有効なJavaクラスファイルを生成することが技術的に不可能な例を挙げてください。

  2. javacまたは他のJavaコンパイラにコンパイル時の依存関係チェックをスキップするように指示する方法を知っていますか?

あなたの答えが両方の質問に対応していることを確認してください。

4

8 に答える 8

20

コンパイル時の依存関係チェックを実行せずに有効なJavaクラスファイルを生成することが技術的に不可能な例を挙げてください。

このコードを考えてみましょう:

public class GotDeps {
  public static void main(String[] args) {
    int i = 1;
    Dep.foo(i);
  }
}

ターゲットメソッドにシグネチャがある場合、public static void foo(int n)次の命令が生成されます。

public static void main(java.lang.String[]);
  Code:
   0:   iconst_1
   1:   istore_1
   2:   iload_1
   3:   invokestatic    #16; //Method Dep.foo:(I)V
   6:   return

ターゲットメソッドにシグニチャがある場合、public static void foo(long n)はメソッド呼び出しintの前にプロモートされます。long

public static void main(java.lang.String[]);
  Code:
   0:   iconst_1
   1:   istore_1
   2:   iload_1
   3:   i2l
   4:   invokestatic    #16; //Method Dep.foo:(J)V
   7:   return

CONSTANT_Methodref_infoこの場合、呼び出し命令や、クラス定数プールで参照される構造体に番号16を入力する方法を生成することはできません。詳細については、VM仕様のクラスファイル形式を参照してください。

于 2009-10-08T13:40:24.823 に答える
6

そのような方法はないと思います。コンパイラは、適切なバイトコードを作成するために、引数のクラスについて知る必要があります。Foobarクラスが見つからない場合、Testクラスをコンパイルできません。

引数を実際に使用していないため、2つのクラスは機能的に同等ですが、同一ではなく、コンパイル時に異なるバイトコードが生成されることに注意してください。

したがって、この場合、コンパイラがコンパイルするクラスを見つける必要がないという前提は正しくありません。

編集-あなたのコメントは、「コンパイラは事実を見逃して、とにかく適切なバイトコードを生成することはできませんか?」と尋ねているようです。

答えはノーです-それはできません。 Java言語仕様によると、メソッドのシグネチャは型をとる必要があります。型は、コンパイル時に解決できるように他の場所で定義されています。

つまり、要求していることを実行するコンパイラを作成することは機械的に非常に簡単ですが、JLSに違反するため、技術的にはJavaコンパイラではありません。その上、コンパイル時の安全性を回避することは、私にとって大きなセールスポイントのようには思えません... :-)

于 2009-10-08T13:18:00.313 に答える
2

Javaタイプのチェックを中断せずにこれを許可する方法がわかりません。メソッドで参照オブジェクトをどのように使用しますか?あなたの例を拡張するために、

class test {
   void foo (pkg.not.in.classpath.FooBar foobar) { 
       foobar.foobarMethod(); //what does the compiler do here?
  } 
}

ライブラリで動作するものでコンパイル(およびメソッドを呼び出す)する必要がある状況では、(メモリからのメソッド呼び出し、不正確な場合があります)

 void foo(Object suspectedFoobar)
     {
       try{
        Method m = suspectedFoobar.getClass().getMethod("foobarMethod");
        m.invoke(suspectedFoobar);
       }
       ...
     }

でも、これをやる意味がよくわかりません。解決しようとしている問題について、より多くの情報を提供できますか?

于 2009-10-08T13:16:49.007 に答える
2

依存するクラスの型アノテーションを確認せずにクラスをコンパイルすることは、JLSの違反になります。準拠したJavaコンパイラではこれを実行できません。

しかし...かなり似たようなことをすることは可能です。具体的には、Aに依存するクラスAとクラスBがある場合、次のことが可能です。

  1. A.javaをコンパイルします
  2. B.javaをA.classに対してコンパイルします。
  3. A.javaを編集して、互換性のない方法で変更します。
  4. 古いA.classを置き換えて、A.javaをコンパイルします。
  5. B.classと新しい(互換性のない)A.classを使用してJavaアプリケーションを実行します。

これを行うとIncompatibleClassChangeError、クラスローダーが署名の非互換性に気付いたときにアプリケーションが失敗します。

実際、これは、依存関係を無視してコンパイルすることがなぜ悪い考えであるかを示しています。一貫性のないバイトコードファイルを使用してアプリケーションを実行すると、(のみ)最初に検出された不一致が報告されます。したがって、多くの不整合がある場合は、それらすべてを「検出」するために、アプリケーションを何度も実行する必要があります。実際、Class.forName()アプリケーションまたはその依存関係のいずれかにクラスの動的ロード(たとえば、を使用)がある場合、これらの問題の一部はすぐには現れない可能性があります。

要約すると、コンパイル時に依存関係を無視するコストは、Java開発の速度を低下させ、Javaアプリケーションの信頼性を低下させます。

于 2009-10-08T14:51:48.033 に答える
1

Javaは設計上、コンパイル時の依存関係チェックを行い、タイプを判別するだけでなく、オーバーロードされたときにメソッド呼び出しを判別するために使用します。私はそれを回避する方法を知りません。

実行できること(および、たとえばJDBCドライバーに対して実行されること)は、リフレクションを使用して依存関係のチェックを遅らせることです。Class.forNameコンパイラがコンパイル時にクラスを認識していなくても、クラスを取得できます。ただし、一般的に、これはコードがインターフェイスに書き込まれ、実行時にインターフェイスを実装するクラスがロードされることを意味します。

于 2009-10-08T13:58:46.830 に答える
0

抽出インターフェース

pkg.in.classpath.IFooBar

FooBar implements IFooBarと_

class Test { void foo(pkg.in.classpath.IFooBar foobar) {} }

Testクラスがコンパイルされます。FooBar工場と構成を使用して、適切な実装をランタイムにプラグインするだけです。いくつかのIOCコンテナを探します。

于 2009-10-08T13:19:53.663 に答える
0

実行できる唯一のことは、バイトコード操作を使用して、より具体的なタイプに変換することです。

pkg.not.in.classpath.FooBarこれを区別するために使用するJava文法には何もありません。

 package pkg.not.in.classpath;
 public class FooBar { }

これから:

 package pkg.not.in.classpath;
 class FooBar { }

したがって、そこでFooBarを使用することが合法であるというあなたの言葉だけがあります。

パッケージスコープのクラスとソースの内部クラスの間にもあいまいさがあります。

class pkg {
    static class not {
        static class in {
            static class classpath {
                static class FooBar {}
            }
        }
    }
}

内部クラスもpkg.not.in.classpath.FooBarソースで呼び出されますが、クラスファイルではpkg$not$in$classpath$FooBarなくとして参照されます。pkg/not/in/classpath/FooBarクラスパスで検索せずに、javacが意味を判断する方法はありません。

于 2009-10-08T13:40:55.417 に答える
0

私は2つのクラスを作成しました:CallerCallee

public class Caller {
    public void doSomething( Callee callee) {
        callee.doSomething();
    }

    public void doSame(Callee callee) {
        callee.doSomething();
    }

    public void doSomethingElse(Callee callee) {
        callee.doSomethingElse();
    }
}

public class Callee {
    public void doSomething() {
    }
    public void doSomethingElse() {
    }
}

これらのクラスをコンパイルしてから、javap -c Callee > Callee.bcとで分解しましたjavap -c Caller > Caller.bc。これにより、次のようになりました。

Compiled from "Caller.java"
public class Caller extends java.lang.Object{
public Caller();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

public void doSomething(Callee);
Code:
0: aload_1
1: invokevirtual #2; //Method Callee.doSomething:()V
4: return

public void doSame(Callee);
Code:
0: aload_1
1: invokevirtual #2; //Method Callee.doSomething:()V
4: return

public void doSomethingElse(Callee);
Code:
0: aload_1
1: invokevirtual #3; //Method Callee.doSomethingElse:()V
4: return

}

Compiled from "Callee.java"
public class Callee extends java.lang.Object{
public Callee();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

public void doSomething();
Code:
0: return

public void doSomethingElse();
Code:
0: return

}

コンパイラは、メソッドシグネチャとinvokevirtual「callee」へのメソッド呼び出しのタイプセーフ呼び出しを生成しました。コンパイラは、ここで呼び出されているクラスとメソッドを認識しています。そのクラスが利用できない場合、コンパイラはメソッドシグネチャまたは「invokevirtual」をどのように生成しますか?

動的呼び出しをサポートする「invokedynamic」オペコードを追加するJSR(JSR 292 )がありますが、これは現在JVMではサポートされていません。

于 2009-10-08T14:53:46.173 に答える