...
Code Block | ||
---|---|---|
| ||
public class Widget { private int noOfComponents; public Widget(int noOfComponents) { this.noOfComponents = noOfComponents; } public int getNoOfComponents() { return noOfComponents; } public final void setNoOfComponents(int noOfComponents) { this.noOfComponents = noOfComponents; } // Also overrides hashCode() (code is omitted) ... public boolean equals(Object o) { if (o == null || !(o instanceof Widget)) { return false; } Widget widget = (Widget) o; // check for negative components return this.noOfComponents == widget.getNoOfComponents(); } @Override if (noOfComponents < 0public || widget.getNoOfComponentsint hashCode() < 0) { int res = return false31; } res = res * 17 + noOfComponents; return this.noOfComponents == widget.getNoOfComponents()res; } } public class LayoutManager { private Set<Widget> layouts = new HashSet<Widget>(); public void addWidget(Widget widget) { if (!layouts.contains(widget)) { layouts.add(widget); } } public int getLayoutSize() { return layouts.size(); } } |
...
Code Block |
---|
public class Navigator extends Widget { public Navigator(int noOfComponents) { super(noOfComponents); } @Override public booleanint equalshashCode(Object o) { // Always returns true int res = 31; res = res * 17; return trueres; } } |
...
Client code
Code Block |
---|
Widget nav = new Navigator(-1); Widget widget = new Widget(101); LayoutManager manager = new LayoutManager(); manager.addWidget(nav); manager.addWidget(widget); System.out.println(manager.getLayoutSize()); // prints 2 |
The set layouts
is expected to contain just one item because the number of components for both the navigator and widget being added is 1. However, the getLayoutSize()
method returns 2.
The reason for this discrepancy is that the equalsthe hashCode()
method of Widget
is not used; instead the equalsused only once. When the navigator is added, the hashCode()
method provided by the Navigator
class is used.
Compliant Solution (final class)
This compliant solution declares the Widget
class final so that its methods cannot be overridden.
Code Block | ||
---|---|---|
| ||
public final class Widget {
// ...
} |
Noncompliant Code Example (run())
What gets printed - main or child / both / either ?
In this noncompliant code example, class Worker
starts a thread in startThread()
method.
Code Block | ||
---|---|---|
| ||
public class TrustedWorker implements Runnable { TrustedWorker() { } public void startThread(String name) { new Thread(this, name).start(); } @Override public void run() { System.out.println("ChildParent"); } } public class UntrustedSubWorker extends TrustedWorker { // Note untrusted code may@Override start a new threadpublic even during construction Untrustedvoid startThread(String name) { super.startThread(name); new Thread(this, name).start(); } @Override public void run() { System.out.println("MainChild"); } } |
Client code:
Code Block |
---|
Trusted a Worker w = new UntrustedSubWorker(); w.startThread("Mainthread"); a.run(); |
The programmer expects Parent
and Child
to be printed, however, Child
is printed twice. This is because the overridden method run()
is invoked both the times when a new thread is started.