有没有办法在Java中重写类变量?
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将打印“爸爸”。 有没有办法让它打印“儿子”?
是。 但是作为变量而言,它会被覆盖(赋予变量新的值,给函数赋予新的定义是Override)。 Just don't declare the variable but initialize (change) in the constructor or static block.
当在父类的块中使用时,该值将得到反映
如果变量是静态的,那么在初始化过程中使用静态块更改该值,
class Son extends Dad { static { me = 'son'; } }
否则改变构造函数。
您也可以稍后在任何块中更改该值。 它会反映在超级班
总之,不,没有办法重写一个类变量。
你不要在Java中重载类变量,你会隐藏它们。 重写是例如方法。 隐藏与重写不同。
在你给出的例子中,通过在类Son中声明类名为“me”的类变量,你可以隐藏类变量,它将从它的超类爸爸继承而来,名字为“我”。 以这种方式隐藏变量不会影响父类父类中的类变量“我”的值。
对于你的问题的第二部分,如何使它打印“儿子”,我会通过构造函数设置值。 虽然下面的代码与你原来的问题有很大的不同,但是我会写这样的东西,
public class Person { private String name; public Person(String name) { this.name = name; } public void printName() { System.out.println(name); } }
JLS在第8.3节- 字段声明中提供了更多关于隐藏的细节
是的,只需重写printMe()
方法:
class Son extends Dad { public static final String me = "son"; @Override public void printMe() { System.out.println(me); } }
您可以创建一个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" }
如果你要覆盖它,我没有看到有效的理由保持这种静态。 我会建议使用抽象(请参阅示例代码)。 :
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爸爸
System.err和上面一样(只是有红色的字体)
JOption输出:
汤米呢
那么南希
爸
这看起来像一个设计缺陷。
除去static关键字,并在构造函数中设置变量。 这样,Son只是在构造函数中将该变量设置为一个不同的值。
虽然类变量只能隐藏在子类中,而不是被覆盖,但是仍然可以在不覆盖子类中的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)); } }
只有通过覆盖printMe()
:
class Son extends Dad { public void printMe() { System.out.println("son"); } }
在Dad.printMe
方法中对me
的引用隐含地指向了静态字段Dad.me
,所以Dad.me
,您都要更改printMe
在Son
。
你不能覆盖类中的变量。 您只能覆盖方法。 你应该保持变量的私有性,否则你可能会遇到很多问题。
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(); }
将打印“儿子”。
https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html
这就是所谓的隐藏字段
从上面的链接
在类中,与超类中的字段具有相同名称的字段隐藏超类的字段,即使它们的类型不同。 在子类中,超类中的字段不能被其简单名称引用。 相反,该字段必须通过超级来访问,这在下一节中将会介绍。 一般来说,我们不建议隐藏字段,因为它使代码难以阅读。
它确实打印“爸爸”,因为这个领域是不是被覆盖,但隐藏。 有三种方法可以打印“儿子”:
方法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(); }
当然,使用私有属性,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置于棘手的境地? 对我来说,被保护的静态字符串我是显然被覆盖,你可以看到当你执行这个程序。 另外,为什么属性不能被覆盖,对我来说没有任何意义。
当你可以很容易地在子类中重新分配变量的时候,你为什么要重写变量呢?
我遵循这种模式来解决语言设计问题。 假设你的框架中有一个重要的服务类,需要在多个派生的应用程序中使用不同的风格。在这种情况下,配置超类逻辑的最好方法是重新分配其“定义”变量。
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"; } }