【问题标题】:Is there a way to override class variables in Java?有没有办法覆盖 Java 中的类变量?
【发布时间】:2010-10-15 16:17:18
【问题描述】:
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”。有没有办法让它打印“儿子”?

【问题讨论】:

  • 如果您看到受保护的静态,请运行。
  • @TomHawtin-tackline 原谅我的无知,但为什么受保护的静态不受欢迎?我尝试谷歌搜索,但找不到明确的答案。谢谢

标签: java inheritance overriding


【解决方案1】:

是的,只需覆盖printMe() 方法:

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

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

【讨论】:

  • 如果您使用的库没有printMe() 方法怎么办,或者即使它有该方法也是静态方法?
  • 为了让这个例子更真实,您可以考虑“printMe”方法可能是一个非常复杂的函数,其逻辑您不想重复。在此示例中,您只想更改人员的姓名,而不是打印它的逻辑。您应该创建一个名为“getName”的方法,您将覆盖该方法以返回“son”,并且 printMe 方法将调用 getName 方法作为其打印的一部分。
【解决方案2】:

只能通过覆盖printMe():

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

Dad.printMe 方法中对me 的引用隐含地指向静态字段Dad.me,因此您正在以一种或另一种方式改变printMeSon 中所做的事情...

【讨论】:

    【解决方案3】:

    这看起来像是一个设计缺陷。

    删除 static 关键字并设置变量,例如在构造函数中。这样,Son 只需在他的构造函数中将变量设置为不同的值。

    【讨论】:

    • 这有什么不妥之处?如果 'me' 成员变量应该在您的设计中被覆盖,那么 patrick's 是正确的解决方案
    • 实际上,如果我对每个实例的值都相同,那么只需删除“静态”就可以了。初始化不必在构造函数中。
    • 对,虽然技术上(在字节码中)我认为它几乎一样;-)
    【解决方案4】:

    您不能覆盖类中的变量。您只能覆盖方法。你应该保持变量私有,否则你会遇到很多问题。

    【讨论】:

    • 你不能覆盖任何静态变量。
    • (或者至少它没有意义,IFSWIM。)
    • 正确,您不能覆盖静态。你可以遮蔽他们,或者有人叫我 MASKING。
    【解决方案5】:

    简而言之,不,没有办法覆盖类变量。

    您不会覆盖 Java 中的类变量,而是隐藏它们。覆盖是实例方法。隐藏不同于覆盖。

    在您给出的示例中,通过在 Son 类中声明名为“me”的类变量,您隐藏了它将从其超类 Dad 继承的具有相同名称的类变量“me”。以这种方式隐藏变量不会影响超类 Dad 中类变量 'me' 的值。

    对于您问题的第二部分,关于如何使其打印“儿子”,我将通过构造函数设置值。尽管下面的代码与您最初的问题有很大的不同,但我会这样写;

    public class Person {
        private String name;
    
        public Person(String name) {
            this.name = name;
        }
    
        public void printName() {
            System.out.println(name);
        }
    }
    

    JLS 在8.3 - Field Declarations 部分提供了更多关于隐藏的详细信息

    【讨论】:

    • 在派生类中有一个静态块可以覆盖
    • @siddagrl:你能详细说明一下吗?
    • 在此示例中应该替换 OP 的哪个类 Person
    • @naxa SonDad 应该继承自 Person,然后在它们的构造函数中调用 super("Son or Dad");
    • 在Java中隐藏这样的变量是好是坏?
    【解决方案6】:

    您可以创建一个 getter,然后覆盖该 getter。如果您要覆盖的变量是其自身的子类,则它特别有用。想象一下,您的超类有一个 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"
    }
    

    【讨论】:

      【解决方案7】:

      当然,使用私有属性,getter 和 setter 会是推荐的做法,但我测试了以下,它的工作原理...查看代码中的注释

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

      Sooo ...我只是重新定义了继承规则还是将 Oracle 置于一个棘手的境地? 对我来说,protected static String me 显然被覆盖了,正如你在执行这个程序时所看到的那样。另外,为什么属性不应该被覆盖对我来说没有任何意义。

      【讨论】:

      • 在这种情况下,您正在“隐藏”超类中的变量。这与覆盖不同,与继承无关。其他类将看到一个变量或另一个变量,具体取决于它们是引用基类还是子类,并且他们看到的变量在编译时是固定的。如果您将子类中的名称“me”更改为其他名称,您将获得相同的效果。大多数 IDE 和代码验证工具(findbugs 等)会在您隐藏此类变量时向您发出警告,因为这通常不是您想要做的。
      • 您只是从编码的角度来看待编程。这很好,在这种情况下你想要的代码。如果您从团队成员的角度来看编码,那么规则就会变得清晰,例如为什么字段不可覆盖等等。我可以就原因发表演讲,但如果你不是从团队成员的角度来看,你只会认为我的观点是无效的,并且会向我抛出无效的论点。
      【解决方案8】:

      虽然类变量确实可能只隐藏在子类中,而不是被覆盖,但仍然可以在不覆盖子类中的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 嗯?... +1
      • 但要小心,如果您声明 me 变量受保护,您还必须先启用对变量的访问权限,然后才能使用它,因为爸爸无权访问儿子的受保护字段。跨度>
      【解决方案9】:
      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();
      }
      

      ...将打印“儿子”。

      【讨论】:

      • 这不是和原来的问题一样吗?
      • 你能解释一下为什么吗? (在你的回答中)
      • 这实际上会改变我在Dad类中的价值,我认为这不是首选。
      【解决方案10】:

      如果您要覆盖它,我看不出保持此静态的正当理由。我建议使用抽象(参见示例代码)。 :

           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

      【讨论】:

      • OP 是关于更改对静态的访问。因此,“我”是一个通用的“爸爸”或“儿子”——不需要为每个爸爸或儿子创建的东西,因此是静态的。
      • 是的,你的权利,我没有看到它是静态的。哈哈,这会将我的答案更改为减少大约 100 行代码,如果完全回答的话。感谢您的提醒
      【解决方案11】:

      是的。但就变量而言,它被覆盖(为变量赋予新值。为函数赋予新定义是覆盖)。只是不要声明变量,而是在构造函数或静态块中初始化(更改)。

      在父类的block中使用时会体现出来的值

      如果变量是静态的,则在初始化期间使用静态块更改值,

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

      或者改变构造函数。

      您也可以稍后在任何块中更改该值。它将反映在超类中

      【讨论】:

      • 我认为答案应该有一些代码以提高可读性。只需将 Son 实现更改为 class Son extends Dad { static { me = 'son' } }
      【解决方案12】:

      它确实打印了'dad',因为该字段没有被覆盖而是隐藏了。有三种方法可以让它打印“儿子”:

      方法一:覆盖 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();
      }
      

      方法二:不隐藏字段,在构造函数中初始化

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

      方法三:在构造函数中使用静态值初始化字段

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

      【讨论】:

        【解决方案13】:

        当您可以轻松地在子类中重新分配变量时,为什么还要覆盖它们。

        我遵循这种模式来解决语言设计问题。假设您的框架中有一个重要的服务类,需要在多个派生应用程序中以不同的方式使用。在这种情况下,配置超类逻辑的最佳方法是重新分配其“定义”变量。

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

        【讨论】:

          【解决方案14】:

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

          这叫做隐藏字段

          来自上面的链接

          在类中,与超类中的字段同名的字段会隐藏超类的字段,即使它们的类型不同。在子类中,超类中的字段不能通过其简单名称来引用。相反,必须通过 super 访问该字段,这将在下一节中介绍。一般来说,我们不建议隐藏字段,因为这会使代码难以阅读。

          【讨论】:

          • 始终欢迎提供指向潜在解决方案的链接,但请add context around the link,以便您的其他用户知道它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。考虑到仅仅是指向外部站点的链接是Why and how are some answers deleted? 的一个可能原因。
          【解决方案15】:

          没有。类变量(也适用于实例变量)在 Java 中不表现出覆盖特性,因为类变量是根据调用对象的类型调用的。在层次结构中添加了一个类(人类)以使其更加清晰。所以现在我们有

          儿子扩展爸爸扩展人类

          在下面的代码中,我们尝试遍历 Human、Dad 和 Son 对象的数组,但它在所有情况下都打印 Human Class 的值,因为调用对象的类型是 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
                  }
              }
          }
          

          将打印

          • 人类
          • 人类
          • 人类

          所以没有覆盖类变量。

          如果我们想从其父类的引用变量中访问实际对象的类变量,我们需要通过将父引用(人类对象)转换为其类型来明确告诉编译器。

              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() 方法)将获得优先权。

          【讨论】:

            【解决方案16】:

            变量不参与覆盖。只有方法可以。方法调用是在运行时解决的,也就是说,调用方法的决定是在运行时做出的,但变量仅在编译时决定。因此,该变量被调用,其引用用于调用而不是运行时对象。

            看看下面的sn-p:

            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
            

            【讨论】:

              【解决方案17】:

              只需在子类构造函数中调用 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。

              【讨论】:

                猜你喜欢
                • 2013-06-24
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-11-22
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多