...
- Reuse the name of a superclass
- Reuse the name of an interface
- Reuse the name of a field defined in a superclass
- Reuse the name of a field that appears in a different scope within the same method
- Reuse the name of a field, type, or another parameter across packages
Noncompliant Code Example (Class Name)
This noncompliant code example implements a class that reuses the name of the class java.util.Vector
. It attempts to introduce a different condition for the isEmpty()
method for interfacing with native legacy code, by overriding the corresponding method in java.util.Vector
.
...
Code Block |
---|
|
class Vector {
private int val = 1;
public boolean isEmpty() {
if(val == 1) { //compares with 1 instead of 0
return true;
} else {
return false;
}
}
//other functionality is same as java.util.Vector
}
// import java.util.Vector; omitted
public class VectorUser {
public static void main(String[] args) {
Vector v = new Vector();
if(v.isEmpty()) {
System.out.println("Vector is empty");
}
}
}
|
Compliant Solution (Class Name)
This compliant solution declares the class Vector
with a different name.
...
Wiki Markup |
---|
Note: When the developer and organization control the original hidden class, in addition to the code being written, it may be preferable to change the design strategy of the original in accordance with Bloch's _Effective Java_ \[[Bloch 2008|AA. Bibliography#Bloch 08]\] "Item 16: Prefer interfaces to abstract classes." Changing the original class into an interface would permit class {{MyVector}} to declare that it implements the hypothetical {{Vector}} interface. This would permit client code that intended to use {{MyVector}} to remain compatible with code that uses the original implementation of {{Vector}}. |
Noncompliant Code Example (Field Shadowing)
This noncompliant code example reuses the name of the val instance field in the scope of an instance method. This behavior can be classified as shadowing.
Code Block |
---|
|
class VectorMyVector {
private int val = 1;
private void doLogic() {
int val;
//...
}
}
|
Compliant Solution (Field Shadowing)
This solution eliminates shadowing by changing the name of the variable defined in method scope.
Code Block |
---|
|
class MyVector {
private int val = 1;
private void doLogic() {
int newValue;
//...
}
}
|
Noncompliant Code Example (Method Shadowing)
Method shadowing in different scopes becomes possible when two or more packages are used. Method shadowing differs from method overloading in that subclasses are allowed to inherit overloadings defined in the base class. It differs from hiding because the methods do not need to be declared static
. It is also distinct from method overriding, as exemplified in this noncompliant code example.
Code Block |
---|
|
package x;
public class A {
void doLogic() { // default accessibility
// print 'A'
}
public static void main(String[] args) {
A a = new y.C();
a.doLogic(); // invokes doLogic() of class x.B and prints 'B'
}
}
|
Code Block |
---|
|
package x;
public class B extends A {
void doLogic() { // default accessibility
// print 'B'
}
}
|
Code Block |
---|
|
package y; // different package
public class C extends x.B { // public accessibility
public void doLogic() {
// print 'C'
}
}
|
Note that class y.C
is accessible from the package x
and so is its doLogic()
method. However, if the main()
method, defined in class A
, tries to polymorphically invoke y.doLogic()
as shown, the override corresponding to class B in package x
takes precedence. This is because the doLogic()
methods in classes x.A
and x.B
are not visible from class y.C
due to the default
access specifier. As a result, the class x.C
is not considered a part of the overriding hierarchy. Note, however, that the code behaves as expected if the access specifiers of all of the methods are changed to public
.
Compliant Solution (Method Shadowing)
Use a different name to indicate that the class residing in another package is not intended to be part of the overriding chain. A programmer can use dotted notation to explicitly specify which class defines the method to be invoked. Avoid reusing names even when all the relevant classes define the affected methods with a public
access specifier; future evolution of one or more of the classes can reduce method accessibility, leading to unexpected results.
Code Block |
---|
|
package x;
public class A {
void doLogic() {
// print 'A'
}
public static void main(String[] args) {
// explicitly invokes doSequence() of class y.C and prints 'C'
y.C.doSequence();
}
}
|
Code Block |
---|
|
package x;
public class B { /* ... */ }
package y; // different package
public class C extends x.B {
public void doSequence() { // now renamed
// print 'C'
}
}
|
...