173
class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";
}

public void doIt()
{
    new Son().printMe();
}

関数 doIt は「お父さん」を出力します。「息子」を印刷する方法はありますか?

4

17 に答える 17

102

つまり、クラス変数をオーバーライドする方法はありません。

Java ではクラス変数をオーバーライドせず、それらを非表示にします。オーバーライドは、インスタンス メソッドです。非表示は上書きとは異なります。

あなたが与えた例では、クラスSonで名前が「me」のクラス変数を宣言することにより、同じ名前の「me」でスーパークラスDadから継承されたクラス変数を非表示にします。この方法で変数を非表示にしても、スーパークラス Dad のクラス変数 'me' の値には影響しません。

「息子」を出力する方法についての質問の2番目の部分については、コンストラクターを介して値を設定します。以下のコードは元の質問からかなり離れていますが、次のように書きます。

public class Person {
    private String name;

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

    public void printName() {
        System.out.println(name);
    }
}

JLS は、セクション8.3 - フィールド宣言で隠蔽に関する詳細を提供します。

于 2009-11-06T11:10:12.393 に答える
73

はい。しかし、変数に関しては上書きです(変数に新しい値を与える。関数に新しい定義を与えることはオーバーライドです)。変数を宣言せずに、コンストラクターまたは静的ブロックで初期化 (変更) します。

親クラスのブロックで使用すると値が反映されます

変数が静的な場合は、初期化中に静的ブロックで値を変更します。

class Son extends Dad {
    static { 
       me = "son"; 
    }
}

または、コンストラクターで変更します。

後で任意のブロックで値を変更することもできます。スーパークラスに反映されます

于 2015-02-01T14:33:46.997 に答える
31

はい、printMe()メソッドをオーバーライドするだけです:

class Son extends Dad {
        public static final String me = "son";

        @Override
        public void printMe() {
                System.out.println(me);
        }
}
于 2009-03-26T11:09:54.233 に答える
17

ゲッターを作成してから、そのゲッターをオーバーライドできます。オーバーライドする変数がそれ自体のサブクラスである場合に特に役立ちます。スーパークラスにメンバーがあると想像してください。Objectしかし、サブクラスでは、これはより明確に定義されていますInteger

class Dad
{
        private static final String me = "dad";

        protected String getMe() {
            return me;
        }

        public void printMe()
        {
                System.out.println(getMe());
        }
}

class Son extends Dad
{
        private static final String me = "son";

        @Override
        protected String getMe() {
            return me;
        }
}

public void doIt()
{
        new Son().printMe(); //Prints "son"
}
于 2011-07-08T17:08:02.900 に答える
12

オーバーライドする場合、これを静的に保持する正当な理由がわかりません。抽象化の使用をお勧めします (コード例を参照)。:

     public interface Person {
        public abstract String getName();
       //this will be different for each person, so no need to make it concrete
        public abstract void setName(String name);
    }

これでお父さんを追加できます。

public class Dad implements Person {

    private String name;

    public Dad(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
    return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

息子:

public class Son implements Person {

    private String name;

    public Son(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

そしてお父さんは素敵な女性に会いました:

public class StepMom implements Person {

    private String name;

    public StepMom(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

私たちには家族がいるようです。彼らの名前を世界に伝えましょう:

public class ConsoleGUI {

    public static void main(String[] args) {
        List<Person> family = new ArrayList<Person>();
        family.add(new Son("Tommy"));
        family.add(new StepMom("Nancy"));
        family.add(new Dad("Dad"));
        for (Person person : family) {
            //using the getName vs printName lets the caller, in this case the
            //ConsoleGUI determine versus being forced to output through the console. 
            System.out.print(person.getName() + " ");
            System.err.print(person.getName() + " ");
            JOptionPane.showMessageDialog(null, person.getName());
    }
}

}

System.out 出力: Tommy Nancy Dad
System.err は上記と同じです (赤いフォントのみ)
JOption 出力:
Tommy then
Nancy then
Dad

于 2014-04-14T01:34:32.997 に答える
6

クラス変数がサブクラスでのみ隠され、オーバーライドされないことは事実ですが、サブクラスでオーバーライドprintMe ()しなくても、必要なことを実行することは可能であり、リフレクションはあなたの友達です。以下のコードでは、わかりやすくするために例外処理を省略しています。meas の宣言protectedは、サブクラスに隠されるため、このコンテキストではあまり意味がないように思われることに注意してください...

class Dad
  {
    static String me = "dad";

    public void printMe ()
      {
        java.lang.reflect.Field field = this.getClass ().getDeclaredField ("me");
        System.out.println (field.get (null));
      }
  }
于 2013-07-15T03:11:47.333 に答える
6

これは設計上の欠陥のようです。

static キーワードを削除し、たとえばコンストラクターで変数を設定します。このように、Son はコンストラクターで変数を別の値に設定するだけです。

于 2009-03-26T11:17:14.993 に答える
3

フィールドは上書きされずに隠されているため、実際には「お父さん」が出力されます。'son' を表示させる方法は 3 つあります。

アプローチ 1: printMe をオーバーライドする

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";

    @override
    public void printMe()
    {
        System.out.println(me);
    }
}

public void doIt()
{
    new Son().printMe();
}

アプローチ 2: フィールドを非表示にせず、コンストラクターで初期化する

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    public Son()
    {
        me = "son";
    }
}

public void doIt()
{
    new Son().printMe();
}

アプローチ 3: static 値を使用してコンストラクターのフィールドを初期化する

class Dad
{
    private static String meInit = "Dad";

    protected String me;

    public Dad() 
    {
       me = meInit;
    }

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    private static String meInit = "son";

    public Son()
    {
        me = meInit;
    }

}

public void doIt()
{
    new Son().printMe();
}
于 2015-04-17T03:06:47.087 に答える
2

オーバーライドすることによってのみprintMe():

class Son extends Dad 
{
    public void printMe() 
    {
        System.out.println("son");
    }
}

meメソッド内の参照はDad.printMe暗黙的に静的フィールドを指しているため、何らかの方法で、内での動作をDad.me変更してprintMeSonます...

于 2009-03-26T11:10:49.737 に答える
2

クラス内の変数をオーバーライドすることはできません。メソッドのみをオーバーライドできます。変数は非公開にしておく必要があります。そうしないと、多くの問題が発生する可能性があります。

于 2009-03-26T11:19:56.360 に答える
1

クラス変数は呼び出し元オブジェクトのタイプに基づいて呼び出されるため、Java ではクラス変数 (インスタンス変数にも適用可能) はオーバーライド機能を示しません。より明確にするために、階層にもう 1 つのクラス (Human) を追加しました。だから今、私たちは持っています

息子は人間を拡張します お父さんは人間を拡張します

以下のコードでは、Human、Dad、および Son オブジェクトの配列を繰り返し処理しようとしていますが、呼び出し元のオブジェクトのタイプが Human であったため、すべてのケースで Human クラスの値が出力されます。

    class Human
{
    static String me = "human";

    public void printMe()
    {
        System.out.println(me);
    }
}
class Dad extends Human
{
    static String me = "dad";

}

class Son extends Dad
{
    static String me = "son";
}


public class ClassVariables {
    public static void main(String[] abc)   {
        Human[] humans = new Human[3];
        humans[0] = new Human();
        humans[1] = new Dad();
        humans[2] = new Son();
        for(Human human: humans)   {
            System.out.println(human.me);        // prints human for all objects
        }
    }
}

印刷します

  • 人間
  • 人間
  • 人間

したがって、クラス変数のオーバーライドはありません。

親クラスの参照変数から実際のオブジェクトのクラス変数にアクセスしたい場合は、親参照 (Human オブジェクト) をその型にキャストすることによって、これをコンパイラに明示的に伝える必要があります。

    System.out.println(((Dad)humans[1]).me);        // prints dad

    System.out.println(((Son)humans[2]).me);        // prints son

印刷します

  • お父さん
  • 息子

この質問のどの部分について:-すでに提案されているように、Son クラスの printMe() メソッドをオーバーライドしてから、呼び出し時に

Son().printMe();

お父さんのクラス変数「me」は、「me」(Son クラス内) の最も近い宣言 (Son クラスの printme() メソッドから) が優先されるため、非表示になります。

于 2018-05-05T16:21:18.623 に答える
0

もちろん、プライベート属性とゲッターとセッターを使用することをお勧めしますが、次のことをテストしたところ、うまくいきました...コードのコメントを参照してください

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";

    /* 
    Adding Method printMe() to this class, outputs son 
    even though Attribute me from class Dad can apparently not be overridden
    */

    public void printMe()
    {
        System.out.println(me);
    }
}

class Tester
{
    public static void main(String[] arg)
    {
        new Son().printMe();
    }
}

すっごく...継承のルールを再定義しただけですか、それともOracleをトリッキーな状況に陥らせましたか? 私にとっては、このプログラムを実行するとわかるように、protected static String me は明らかにオーバーライドされています。また、なぜ属性をオーバーライドできないのか、私には意味がありません。

于 2012-07-17T12:24:36.090 に答える
0

サブクラスで変数を簡単に再割り当てできるのに、なぜ変数をオーバーライドしたいのでしょうか。

私はこのパターンに従って、言語設計を回避します。フレームワークに、複数の派生アプリケーションのさまざまなフレーバーで使用する必要がある重要なサービス クラスがある場合を想定します。その場合、スーパー クラス ロジックを構成する最善の方法は、その「定義」変数を再割り当てすることです。

public interface ExtensibleService{
void init();
}

public class WeightyLogicService implements ExtensibleService{
    private String directoryPath="c:\hello";

    public void doLogic(){
         //never forget to call init() before invocation or build safeguards
         init();
       //some logic goes here
   }

   public void init(){}    

}

public class WeightyLogicService_myAdaptation extends WeightyLogicService {
   @Override
   public void init(){
    directoryPath="c:\my_hello";
   }

}
于 2015-06-10T14:47:00.783 に答える