Javaでクラス変数をオーバーライドする方法はありますか?


161
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は "dad"を出力します。「息子」を印刷する方法はありますか?


111
保護されたstaticが表示された場合は、実行してください。
トムホーティン-タックライン2009年

23
@ TomHawtin-tacklineは私の無知を許しますが、なぜ保護された静電気が不快なのですか?グーグルで試しましたが、明確な答えが見つかりません。ありがとう
トニーチャン

16
この質問を確認してください:Javaで保護されたstaticを使用すべきではない理由
Zeeshan

回答:


68

はい。しかし、変数に関しては上書きされます(変数に新しい値を与える。関数に新しい定義を与えることはOverrideです)。Just don't declare the variable but initialize (change) in the constructor or static block.

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

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

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

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

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


10
読みやすさを向上させるために、答えにはコードが必要です。ちょうどに息子の実装を変更 class Son extends Dad { static { me = 'son' } }
Cléssioメンデス

97

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

非表示にするJavaのクラス変数はオーバーライドしません。オーバーライドはインスタンスメソッド用です。非表示は上書きとは異なります。

あなたが与えた例では、Sonクラスで 'me'という名前のクラス変数を宣言することにより、同じ名前 'me'を持つスーパークラス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-フィールド宣言で非表示の詳細を説明しています。


1
派生クラスに静的ブロックがあることでオーバーライドが可能です
siddagrl

1
@siddagrl:詳しく説明できますか?
Mr_and_Mrs_D 2013年

Personこの例では、OPのどのクラスが置き換えられることになっていますか?
n611x007 2014年

1
@naxa SonおよびDadから継承することになっておりPersonsuper("Son or Dad");それらのコンストラクタを呼び出します。
Panzercrisis 14年

Javaでそのような変数を非表示にすることは良いことか悪いことか?
Panzercrisis 14年

31

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

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

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

使用しているライブラリにprintMe()メソッドがない場合、またはそのメソッドが静的メソッドである場合でもどうなりますか?
Venkat Sudheer Reddy Aedama

1
これをより現実的な例にするために、「printMe」メソッドは、繰り返したくないロジックを持つ非常に複雑な関数であると考えることができます。この例では、人の名前を変更するだけで、印刷するロジックは変更しません。「son」を返すようにオーバーライドする「getName」というメソッドを作成する必要があります。printMeメソッドは、印刷の一部としてgetNameメソッドを呼び出します。
リング

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"
}

11

あなたがそれをオーバーライドしようとしているのなら、私はこれを静的に保つ正当な理由がわかりません。抽象化の使用をお勧めします(コード例を参照)。:

     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
Nancy
Dad


1
OPはstaticsへのアクセスの変更に関するものでした。したがって、「私」は一般的な「お父さん」または「息子」でした- すべてのお父さんや息子のため作成する必要はなく、したがって静的です。
RichieHH 2014年

ええ、あなたの権利、私はそれが静的であるとは知りませんでした。はは、それが私の答えを約100行少ないコード行に変更するでしょう。頭を上げてくれてありがとう
nckbrz

6

これは設計上の欠陥のように見えます。

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


1
これについて何が間違っていますか?'me'メンバー変数がデザインでオーバーライド可能であると想定されている場合、patrickが正しい解決策です
Chii

実際、すべてのインスタンスで私の値が同じであれば、「静的」を削除するだけで十分です。初期化はコンストラクターで行う必要はありません。
ネイトパーソンズ

そうです、技術的には(バイトコードで)ほぼ同じだと思います;-)
Patrick Cornelissen

4

クラス変数はサブクラスでのみ非表示にでき、オーバーライドできないことは事実ですが、サブクラスでオーバーライドせずに必要なことを実行することは可能printMe ()であり、リフレクションは友です。以下のコードでは、わかりやすくするために例外処理を省略しています。宣言していることに注意してくださいmeとすると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));
      }
  }

非常に興味深い、
Java.lang.reflect

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

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

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

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

...「息子」を印刷します。


これは元の質問と同じではありませんか?
Corley Brigman 2013年

理由を説明できますか?(あなたの答えで)
Mr_and_Mrs_D 2013年

3

https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html

それはフィールドを隠すと呼ばれています

上記のリンクから

クラス内で、スーパークラスのフィールドと同じ名前を持つフィールドは、タイプが異なっていても、スーパークラスのフィールドを非表示にします。サブクラス内では、スーパークラスのフィールドを単純な名前で参照することはできません。代わりに、フィールドは、次のセクションで説明するスーパーを通じてアクセスする必要があります。一般的に、コードを読みにくくするため、フィールドを非表示にすることはお勧めしません。


1
可能性のあるソリューションへのリンクはいつでも歓迎しますが、リンクの前後にコンテキスト追加して、他のユーザーがそれが何であるか、なぜそこにあるのかを理解できるようにしてください。ターゲットサイトに到達できない、または永久にオフラインになる場合に備えて、常に重要なリンクの最も関連性の高い部分を引用してください。外部サイトへのリンク以上のものではない理由と、一部の回答が削除される理由について考えられる理由を考慮に入れてください
FelixSFD 2017年

2

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

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

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


2

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


静的をオーバーライドすることはできません。
トム・ホーティン-タックライン2009年

(あるいは少なくともそれは、IFSWIMを意味がありません。)
トムホーティン- tackline

正解です。静的変数をオーバーライドすることはできません。あなたはそれらをシャドウすることができますか、何人かの人々は私をマスキングと呼びました。
Dale

2

フィールドはオーバーライドされずに隠されているため、実際には「お父さん」と表示されます。「息子」を印刷するには、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:静的な値を使用してコンストラクターのフィールドを初期化する

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();
}

2

変数はオーバーリンディングには参加しません。メソッドだけが行います。メソッド呼び出しは実行時に解決されます。つまり、メソッドを呼び出すかどうかの決定は実行時に行われますが、変数はコンパイル時にのみ決定されます。したがって、その変数が呼び出されます。その参照は、ランタイムオブジェクトではなく、呼び出しに使用されます。

次のスニペットを見てください:

package com.demo;

class Bike {
  int max_speed = 90;
  public void disp_speed() {
    System.out.println("Inside bike");
 }
}

public class Honda_bikes extends Bike {
  int max_speed = 150;
  public void disp_speed() {
    System.out.println("Inside Honda");
}

public static void main(String[] args) {
    Honda_bikes obj1 = new Honda_bikes();
    Bike obj2 = new Honda_bikes();
    Bike obj3 = new Bike();

    obj1.disp_speed();
    obj2.disp_speed();
    obj3.disp_speed();

    System.out.println("Max_Speed = " + obj1.max_speed);
    System.out.println("Max_Speed = " + obj2.max_speed);
    System.out.println("Max_Speed = " + obj3.max_speed);
  }

}

コードを実行すると、コンソールに次のように表示されます。

Inside Honda
Inside Honda
Inside bike

Max_Speed = 150
Max_Speed = 90
Max_Speed = 90

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をトリッキーな状況に置きましたか?私には、このプログラムを実行したときにわかるように、保護された静的String meは明らかにオーバーライドされています。また、属性がオーバーライド可能であってはならない理由は、私には何の意味もありません。


1
この場合、スーパークラスから変数を「非表示」にしています。これはオーバーライドとは異なり、継承とは関係ありません。他のクラスは、基本クラスとサブクラスのどちらを参照しているかに応じて、1つの変数または他の変数を参照し、それらが参照する変数はコンパイル時に修正されます。サブクラスの名前「me」を別の名前に変更した場合も、同じ効果が得られます。ほとんどのIDEとコード検証ツール(findbugsなど)は、このように変数を非表示にすると警告を表示します。これは通常、実行したくないことだからです。
AutomatedMike、

あなたはプログラミングだけの観点からプログラミングを見ています。これは問題ありませんが、その場合はコードを使用します。チームメンバーの視点からコーディングを見ると、フィールドがオーバーライドできない理由など、ルールが明確になります。理由についてスピーチをすることができますが、チームメンバーの観点からそれが見られない場合は、私のポイントが無効であると見なされ、無効な引数が返されます。
nckbrz 2014

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";
   }

}

0

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

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

以下のコードでは、Human、Dad、Sonオブジェクトの配列を反復処理しようとしますが、呼び出しオブジェクトのタイプがHumanであるため、すべてのケースでHuman Classの値を出力します。

    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
        }
    }
}

印刷します

  • 人間
  • 人間
  • 人間

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

親クラスの参照変数から実際のオブジェクトのクラス変数にアクセスする場合は、親参照(ヒューマンオブジェクト)をその型にキャストすることにより、これをコンパイラーに明示的に通知する必要があります。

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

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

印刷します

  • パパ
  • 息子

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

Son().printMe();

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


0

サブクラスコンストラクターでsuper.variableを呼び出すだけ

public abstract class Beverage {

int cost;


int getCost() {

    return cost;

}

}`

public class Coffee extends Beverage {


int cost = 10;
Coffee(){
    super.cost = cost;
}


}`

public class Driver {

public static void main(String[] args) {

    Beverage coffee = new Coffee();

    System.out.println(coffee.getCost());

}

}

出力は10です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.