質問

Javaで次のインターフェースを検討してください:

public interface I {
    public final String KEY = "a";
}

次のクラス:

public class A implements I {
    public String KEY = "b";

    public String getKey() {
        return KEY;
    }
}

クラスAが来て、インターフェイスIの最終的な定数をオーバーライドできるのはなぜですか?

自分で試してください:

A a = new A();
String s = a.getKey(); // returns "b"!!!
役に立ちましたか?

解決

変数をシャドウイングしているという事実にもかかわらず、こちら

  

Java 5-" final&quot ;;もう決まっていない

     

ノルウェーのMachina NetworksのNarve Saetreが昨日、私にメモを送りました。   ハンドルを変更できるのは残念だったと言って   最終的な配列。私は彼を誤解し、辛抱強く説明し始めました   配列を定数にすることができず、   配列の内容を保護します。 「いいえ」と彼は言った、「私たちは   リフレクションを使用した最終的なハンドル。"

     

Narveのサンプルコードを試してみましたが、信じられないほど、Java 5で次のことができました。   最終的なハンドル、原始的なフィールドへのハンドルさえも修正してください!私はそれを知っていました   以前はある時点で許可されていましたが、その後は許可されませんでした。   そのため、古いバージョンのJavaでいくつかのテストを実行しました。まず、   最終フィールドを持つクラス:

public class Person {
  private final String name;
  private final int age;
  private final int iq = 110;
  private final Object country = "South Africa";

  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String toString() {
    return name + ", " + age + " of IQ=" + iq + " from " + country;
  }
}
     

JDK 1.1.x

     

JDK 1.1.xでは、次を使用してプライベートフィールドにアクセスできませんでした   反射。ただし、publicを使用して別のPersonを作成できます   フィールド、それに対してクラスをコンパイルし、Personを交換します   クラス。実行中の場合、実行時にアクセスチェックはありませんでした   コンパイルしたクラスとは異なるクラスに対して。   ただし、実行時に最終フィールドを再バインドすることはできませんでした   クラスの交換またはリフレクション。

     

java.lang.reflect.FieldのJDK 1.1.8 JavaDocsには次のものがありました   言う:

     
      
  • このFieldオブジェクトがJava言語アクセス制御を実施し、基礎となるフィールドにアクセスできない場合、メソッドは   IllegalAccessException。
  •   
  • 基になるフィールドがfinalの場合、メソッドはIllegalAccessExceptionをスローします。
  •   
     

JDK 1.2.x

     

JDK 1.2.xでは、これは少し変更されました。プライベートフィールドを作成できるようになりました   setAccessible(true)メソッドでアクセスできます。フィールドへのアクセスは   実行時にチェックされるようになったため、クラススワッピングトリックを使用できませんでした   プライベートフィールドにアクセスします。しかし、私たちは今、突然ファイナルを再バインドすることができました   フィールド!このコードを見てください:

import java.lang.reflect.Field;

public class FinalFieldChange {
  private static void change(Person p, String name, Object value)
      throws NoSuchFieldException, IllegalAccessException {
    Field firstNameField = Person.class.getDeclaredField(name);
    firstNameField.setAccessible(true);
    firstNameField.set(p, value);
  }
  public static void main(String[] args) throws Exception {
    Person heinz = new Person("Heinz Kabutz", 32);
    change(heinz, "name", "Ng Keng Yap");
    change(heinz, "age", new Integer(27));
    change(heinz, "iq", new Integer(150));
    change(heinz, "country", "Malaysia");
    System.out.println(heinz);
  }
}
     

JDK 1.2.2_014でこれを実行すると、次の結果が得られました。

Ng Keng Yap, 27 of IQ=110 from Malaysia    Note, no exceptions, no complaints, and an incorrect IQ result. It seems that if we set a
     

宣言時のプリミティブの最終フィールド、値はインライン化され、   タイプがプリミティブまたはストリングの場合。

     

JDK 1.3.xおよび1.4.x

     

JDK 1.3.xでは、Sunはアクセスを少し強化し、   リフレクションで最終フィールドを変更します。これもそうでした   JDK1.4.x。 FinalFieldChangeクラスを実行して再バインドしようとした場合   実行時にリフレクションを使用して最終フィールドを取得すると、次のようになります。

     
    

javaバージョン" 1.3.1_12&quot ;:例外スレッド" main"     IllegalAccessException:フィールドは最終です             java.lang.reflect.Field.set(ネイティブメソッド)             FinalFieldChange.change(FinalFieldChange.java:8)             FinalFieldChange.main(FinalFieldChange.java:12)で

         

javaバージョン" 1.4.2_05"例外スレッド" main"     IllegalAccessException:フィールドは最終です             java.lang.reflect.Field.set(Field.java:519)             FinalFieldChange.change(FinalFieldChange.java:8)             FinalFieldChange.main(FinalFieldChange.java:12)で

  
     

JDK 5.x

     

これで、JDK 5.xに到達しました。 FinalFieldChangeクラスの出力は同じです   JDK 1.2.xの場合:

Ng Keng Yap, 27 of IQ=110 from Malaysia    When Narve Saetre mailed me that he managed to change a final field in JDK 5 using
     

反射、バグがJDKに忍び込んできたことを望んでいました。しかしながら、   私たちは両方とも、特にそのような根本的なバグではないと考えました。   いくつかの検索の後、JSR-133:Java Memory Modelと   スレッド仕様。仕様のほとんどはハードレアです

他のヒント

非表示にしているのは、「スコープ」の機能です。小さいスコープにいるときはいつでも、好きな変数をすべて再定義でき、外側のスコープ変数は「シャドウ」になります

ところで、必要に応じて再度スコープできます:

public class A implements I {
    public String KEY = "b";

    public String getKey() {
        String KEY = "c";
        return KEY;
    }
}

今、KEYは" c&quot ;;を返します

再読み込み時にオリジナルが吸い込まれたため編集。

クラスは、変数を上書きするのではなく、単に変数を非表示にしているようです:

public class A implements I {
    public String   KEY = "B";

    public static void main(String args[])
    {
        A t = new A();
        System.out.println(t.KEY);
        System.out.println(((I) t).KEY);
    }
}

これにより、「B」および「A」が印刷されます。 A.KEY変数はfinalとして定義されていないため、それに割り当てることもできます。

 A.KEY="C" <-- this compiles.

しかし-

public class C implements I{

    public static void main (String args[])
    {
        C t = new C();
        c.KEY="V"; <--- compiler error ! can't assign to final

    }
}

この方法で定数にアクセスしないでください。代わりに静的参照を使用してください。

I.KEY //returns "a"
B.KEY //returns "b"

設計上の考慮事項として、

public interface I {
    public final String KEY = "a";
}

静的メソッドは常に親キーを返します。

public class A implements I {
    public String KEY = "b";

    public String getKey() {
        return KEY; // returns "b"
    }

    public static String getParentKey(){
        return KEY; // returns "a"
    }
}

Jomが気づいたように。再定義されたインターフェイスメンバを使用した静的メソッドの設計は、大きな問題になる可能性があります。一般に、定数に同じ名前を使用しないようにしてください。

静的フィールドとメソッドは、それらを宣言するクラス/インターフェースにアタッチされます(ただし、実装が必要な完全に抽象クラスであるため、インターフェースは静的メソッドを宣言できません)。

したがって、public static(vartype)(varname)を持つインターフェイスがある場合、 そのフィールドはそのインターフェースに添付されます。

そのインターフェイスを実装するクラスがある場合、コンパイラトリックは(this。)varnameをInterfaceName.varnameに変換します。ただし、クラスでvarnameを再定義すると、varnameという名前の新しい定数がクラスに付加され、コンパイラは(this。)varnameをNewClass.varnameに変換するようになります。メソッドにも同じことが当てはまります。新しいクラスがメソッドを再定義しない場合、(this。)methodNameはSuperClass.methodNameに変換されます。それ以外の場合、(this。)methodNameはCurrentClass.methodNameに変換されます。

このため、「xフィールド/メソッドは静的にアクセスする必要があります」という警告が表示されます。コンパイラは、トリックを使用しても、読みやすくするために明示的であるため、ClassName.method / fieldNameを使用することをお勧めします。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top