Hi, Habr! I present to your attention the translation of the Sub article Bug article by Lukas Eder.
Visibility rules in Java can sometimes be slightly confusing and incomprehensible. What do you think will be displayed after running this code?
package p; import static pAx; class A { static String x = "Ax"; } class B { String x = "Bx"; } class C { String x = "Cx"; class D extends B { void m() { System.out.println(x); } } } public class X { public static void main(String[] args) { new C().new D().m(); } }
It will be displayed:
')
Bx
Because:
B C, , , A.
How can all this lead to errors?
The problem is not that the above code example is cleverly written by itself. Not. Just when you write according to this logic, everything will work exactly as expected. But what happens if we change something? Well, for example, if you change the access modifier of a member of a superclass to
private :
package p; import static pAx; class A { static String x = "Ax"; } class B { private String x = "Bx";
Now, oddly enough, Bx is no longer visible for the m () method, in this case another rule applies. Namely:
()
And in the end we get the following result:
Cx
Of course, we can make some changes again and get the following code:
package p; import static pAx; class A { static String x = "Ax"; } class B { private String x = "Bx"; } class C { String xOld = "Cx";
As we all know, 50% of the variables that will no longer be used are renamed and receive the prefix “old”.
In this final version of the code, the only possible x value remains in the m () method call and this is a statically imported Ax. Thus, the output will be as follows:
Ax
Subtleties that should be considered when working with large software codes
This refactoring showed us that limiting the visibility of an existing element is dangerous enough, since the work of a subclass could depend on it, but by coincidence, the code has another, "less important" and more visible member, having the same name that is included into work and does not give you a compile error.
The same thing happens when you expand the scope of a member of a class that previously had private access modifier. Because of this, he may be in the scope of all his subclasses, although you might not want to.
With static imports, you may also encounter a similar problem. When your code depends on a static import, which suddenly turns out to be hidden by another member with an identical name, for example, a member of a superclass. The author of the changes made may not even notice this, because when making changes, most likely, he will not take into account the presence of subclasses.
Conclusion
In conclusion, I would like to once again note that you should not create subtypes too often. If you can declare the classes to be created as
final , then no one can inherit from them and, as a result, receive a sudden "surprise" when you add new members to the superclass. In addition, every time you make a change in the scope of existing members of a class, be extremely careful and remember the potential possibility of having members with the same names as the one being changed.