5

ここで、Java では、名前が同じで型が異なる 2 つの変数が同じスコープ内に共存できることを読みました。私が意味するのはこれです

class test
{
    private int x;
    private double x;
}

しかし、すべての Java IDE はそのようなコードを許可していません。そのようなコードが本当に構文的に正しいのか、それとも単純に IDE がそのようなコードがあいまいさを防ぐことを許可していないのかを知りたいです。

とにかく、ここにウェブサイトからの抜粋があります

「運が良ければ、Jad からの出力を再コンパイルできる可能性があります。ただし、Java VM には、Java 言語自体よりも変数の命名に関するより寛大な規則があります。たとえば、有効なクラス ファイルには、「a」という名前の複数の変数を含めることができます。そのようなクラスを逆コンパイルすると、得られるソース コードは有効ではなくなります。

JAD は通常、問題のあるフィールドの名前を変更し、再コンパイル可能なファイルを作成します...唯一の問題は、再コンパイルされたファイルが元のクラスと互換性がないことです."

4

4 に答える 4

5

他の人が言ったように、Java では違法ですが、バイトコードでは合法です。

javac アサート

assertは Java の例であり、Oracle JDK 1.8.0_45 で同じ名前でタイプが異なる複数のフィールドを生成します。例えば:

public class Assert {
    // We can't use a primitive like int here or it would get inlined.
    static final int[] $assertionsDisabled = new int[0];
    public static void main(String[] args) {
        System.out.println($assertionsDisabled.length);
        // currentTimeMillis so it won't get optimized away.
        assert System.currentTimeMillis() == 0L;
    }
}

が存在すると、メソッド呼び出しをキャッシュする合成フィールドがassert生成されます。詳細については、 https ://stackoverflow.com/a/29439538/895245を参照してください。bool $assertionsDisable

それで:

javac Assert.java
javap -c -constants -private -verbose Assert.class

次の行が含まれます。

 #3 = Fieldref           #9.#28         // Assert.$assertionsDisabled:[I
 #5 = Fieldref           #9.#31         // Assert.$assertionsDisabled:Z
#12 = Utf8               $assertionsDisabled
#28 = NameAndType        #12:#13        // $assertionsDisabled:[I
#31 = NameAndType        #12:#14        // $assertionsDisabled:Z

public static void main(java.lang.String[]);
 3: getstatic     #3                  // Field $assertionsDisabled:[I
10: getstatic     #5                  // Field $assertionsDisabled:Z

定数テーブル#12が変数名として再利用されていることに注意してください。

ただし、別のブール値を宣言した場合は、コンパイルされません。

static final boolean $assertionsDisabled = false;

エラーあり:

the symbol $assertionsDisabled conflicts with a compile synthesized symbol

これは、フィールド名にドル記号を使用することが非常に悪い考えである理由でもあります:変数名でドル記号 ($) を使用する必要があるのはいつですか?

ジャスミン

もちろん、Jasmin で試すこともできます。

.class public FieldOverload
.super java/lang/Object

.field static f I
.field static f F

.method public static main([Ljava/lang/String;)V
    .limit stack 2

    ldc 1
    putstatic FieldOverload/f I
    ldc 1.5
    putstatic FieldOverload/f F

    getstatic java/lang/System/out Ljava/io/PrintStream;
    getstatic FieldOverload/f I
    invokevirtual java/io/PrintStream/println(I)V

    getstatic java/lang/System/out Ljava/io/PrintStream;
    getstatic FieldOverload/f F
    invokevirtual java/io/PrintStream/println(F)V

    return
.end method

int1 つ( I) と 1 つfloat( )の 2 つの静的フィールドを含みF、出力は次のとおりです。

1
1.5

次の理由で機能する場合:

  • getstaticFieldref定数テーブルの構造体を指します
  • Fieldrefを指すNameAndType
  • NameAndType明らかに型を指す

したがって、それらを区別するために、Jasmin は単純Fieldrefに異なるタイプの 2 つの異なるものを使用します。

于 2015-07-11T09:20:40.403 に答える
3

言語の仕様によると ( JLS 8.3 ):

クラス宣言の本体で同じ名前の 2 つのフィールドを宣言すると、コンパイル エラーになります。

あなたが引用したステートメントは、クラス ファイル (つまり、ソース コードではなく、コンパイルされたファイル) に関するものです。

于 2012-12-01T06:29:36.270 に答える
3

同じ名前 (ただし型は異なる) の変数を同じスコープに存在させることはできません。それが可能かどうかを検討してください。Javaコンパイラは、あなたが意味するものをどのように判断しますか。

このコードスニペットを検討してください

class test
{
    private int x;
    private double x;

    test() //constructor
    {
        System.out.println(x); //Error cannot determine which x you meant
    } 
}

Java コンパイラは、実際に参照している x を理解できません。したがって、そのようなコードは構文的に正しくなく、コンパイルできません。

ただし、生成されたクラス ファイルを作成後に変更できるClassEditorなどのツールが存在します。そこで、2 つの変数の名前を同じものに変更することができます。

ただし、そのようなクラスは必ずしも Java jvm で実行できるとは限りません。

あなたが引用したソフトウェアは、つまりJAD、クラスファイル内のそのような重複した名前付き変数の名前を変更して、取得するソースコードが実際に構文的に正しいようにすることができます

于 2012-12-04T05:27:36.183 に答える
1

もちろん、同じ名前とパラメーター リストを持つ 2 つのメソッドを持つことができないのと同様に、同じクラスに int x フィールドと long x フィールドを持つことはできません。ただし、これはソース レベルです。JVM とバイトコードには異なるルールがあります。このことを考慮:

パッケージテスト;

public class Test {
    static int x;

    @Override
    protected Test clone() throws CloneNotSupportedException {
        return this;
    }

    public static void main(String[] args) {
        int y = x;
    }
}

バイトコード アウトライン ツール (私は Andrey Loskutov の Eclipse 用プラグインを使用しました) を使用すると、この行に int Test.main() が表示されます。

GETSTATIC test/Test.x : I

これは、JVM がフィールド x から値をロードする方法です。そのフルネームは「test/Test.x : I」です。これにより、フィールド値がフィールドのフルネームに存在するというヒントが得られます。

javac がクラスを作成する唯一の手段ではないことは秘密ではありません。バイトコードを直接作成するツール/ライブラリがあり、同じ名前で異なる型のフィールドを持つクラスを自由に作成できます。JNIも同様です。

私が証明しようとしていることの実例を示すのは難しいです。しかし、同様の問題であるメソッドを考えてみましょう。バイトコード分析は、Test クラスに、JLS で許可されていない同じ名前とパラメーターを持つ 2 つのメソッドがあることを示しています。

protected clone()Ltest/Test; throws java/lang/CloneNotSupportedException 

protected volatile bridge clone()Ljava/lang/Object; throws java/lang/CloneNotSupportedException 

「ブリッジ」メソッドは javac によって追加されました。これは、Test.clone() が Test を返すためです。これは、Object を返す Object.clone() をオーバーライドしないことを意味します。これは、JVM がこれら 2 つが異なるメソッドであると考えるためです。

1 clone()Ltest/Test;
2 clone()Ljava/lang/Object;
于 2012-12-01T09:25:18.140 に答える