有没有一种方法可以在Java中覆盖类变量?

174
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"。是否有一种方法使它输出字符串"son"?


123
如果你看到一个被保护的静态变量,就赶紧跑。 - Tom Hawtin - tackline
25
@TomHawtin-tackline,对不起我的无知,但为什么受保护的静态成员会被看作是不好的?我试着用谷歌搜索,但没有找到明确的答案。谢谢。 - Tony Chan
18
请查看这个问题:[为什么我们不应该在Java中使用protected static] (https://dev59.com/_mAf5IYBdhLWcg3wyVB1)。 - Zeeshan
17个回答

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

... 将会打印出 "son"。


这不是与原始问题相同吗? - Corley Brigman
你能解释一下为什么吗?(在回答中) - Mr_and_Mrs_D
这实际上会改变Dad类中我的值,我认为这不是首选的。 - Rik Schaaf

2

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


你不能覆盖任何静态内容。 - Tom Hawtin - tackline
(或者至少不要出错) - Tom Hawtin - tackline
正确的说法是,您不能覆盖静态变量。您可以对它们进行遮蔽或者有些人称之为掩蔽。 - Dale

2

只有通过重写 printMe() 方法:

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

Dad.printMe 方法中对 me 的引用隐式地指向静态字段 Dad.me,所以无论如何你都会改变 SonprintMe 的行为...


1
类变量(也适用于实例变量)在Java中不具有覆盖特性,因为类变量是根据调用对象的类型来调用的。为了使情况更加清晰,在层次结构中添加了一个类(Human)。所以现在我们有:
Son继承Dad继承Human
在下面的代码中,我们尝试遍历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
        }
    }
}

将打印

  • 人类
  • 人类
  • 人类

因此没有覆盖类变量。

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

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

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

将会打印

  • 父亲
  • 儿子

关于这个问题的一部分:- 如已建议,在Son类中覆盖printMe()方法,然后在调用时

Son().printMe();

父类变量"me"将被隐藏,因为最近的声明(在儿子类的printme()方法中)的"me"(在儿子类中)将优先。


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


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
当然,使用私有属性和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();
    }
}

所以...我刚刚是重新定义了继承规则还是让Oracle陷入了棘手的境地? 对我来说,protected static String me 明显被覆盖了,当你执行这个程序时就可以看到。而且,对我来说,为什么属性不能被覆盖没有任何意义。


1
在这种情况下,您正在从超类“隐藏”变量。这与覆盖不同,与继承无关。其他类将看到一个变量或另一个变量,具体取决于它们引用基类还是子类,并且他们看到的变量在编译时固定。如果您在子类中更改名称“me”为其他内容,则会获得相同的效果。大多数IDE和代码验证工具(例如findbugs)都会在隐藏此类变量时发出警告,因为这通常不是您想要做的事情。 - AutomatedMike
你只是从编码的角度来看待编程。这没问题,如果是这样,请随意编写代码。但如果你从团队成员的角度来看待编码,那么规则就变得清晰明了,比如为什么字段不能被覆盖等等。我可以向你解释原因,但如果你没有从团队成员的角度来看待它,你只会认为我的观点是无效的,并且回应我无效的论点。 - nckbrz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接