Reputation: 175
I'm creating a Node class for use in two similar but fundamentally different algorithms. Having some common functionality between them my idea was to create an abstract node class with shared methods and fields. Extend from this abstract class two concrete subclass's WaveNode and EchoNode.
Some of the methods in the abstract class deal with instances of a Node, but I wanted to use this common code with instance of subclass's, that is if you give the method a WaveNode or an EchoNode then the method doesn't need a different implementation. So I thought it best to implement it in the abstract class and both subclasses can use the implementation so I don't have to type it out twice. However when I'm in my subclass's and I'm dealing with a WaveNode or EchoNode, I'm getting compile errors because the method expects an abstract Node instance. Is there a way to implement a method in a super class and different classes extending the super class can use its implementation.
An example is below.
Set<Node> getNeighs(){
Set<Node> nei = (Set<Node>) rec.keySet();
nei.remove(this);
return nei;
}
This code takes a map "rec" and puts the keyset (of Node) into a Set of Node. Removes the current node and returns all of its neighbours. So both WaveNode and EchoNode use exactly the same code. The only different is the Set would be of WaveNode or EchoNode. I wanted to implement it with Node in the superclass to save me writing it out twice. Is it possible?
edit
Posting some more of the code:
public abstract class Node {
private final int id;
Map<Node, Boolean> rec = new HashMap<Node, Boolean>();
public Node(int id) {
this.id = id;
}
int getId() {
return id;
}
void addNeigh(Node neigh) {
rec.put(neigh, false);
}
Set<Node> getNeighs() {
Set<Node> nei = (Set<Node>) rec.keySet();
nei.remove(this);
return nei;
}
void printNeighbours() {
Set<Node> nei = getNeighs();
System.out.println(this +" neighbours are: " + nei);
}
Node getSilentNeigh() {
for(Entry<Node, Boolean> entry : rec.entrySet())
{
if(!entry.getValue())
return entry.getKey();
}
return null;
}
public final class TreeNode extends Node {
boolean messageSent = false;
public TreeNode(int id){
super(id);
}
public void sendTok(TreeNode sender){
rec.put(sender, true);
}
Please be aware that I have it working as intended now, it was my own fault for not casting the return type of Node to TreeNode. However any comments on my code "doing too much" or similar advice on cleaning up my code is welcome. Thanks
Upvotes: 0
Views: 921
Reputation: 126
trying to be more helpful, i tried to intuit the spirit of the problem and come up with a workable solution. there are many more sophisticated points the code COULD have done (cf., Nosretep above), but too much detail can detract from a beginner learning the main point; therefore, this code is only suggestive of a simple working approach.
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
/* this class does everything that EVERY Node does since it defines what that means; for
* ANY behaviors that are common to subclasses, here is where they should be implemented.
*/
abstract class Node {
private final int id;
private boolean tokenSent = false;
public Node(int id) {
this.id = id;
}
int getId() {
return id;
}
// common behavior: other things that a Node might wish to do in context of the problem
public void sendTok() {
if (!tokenSent) {
// send token here
tokenSent = true;
}
}
/* common behavior: this is not really the ideal way to do this, but hopefully it makes
* sense in the context of the problem being solved; better would be an iterator that
* visits each node in the list and performs the printing, etc., but this is in the
* spirit of the problem
*/
public void printNeighboursOf(List<Node> list) {
if (list.size() > 1) {
System.out.print(this + "[" + getId() + "] has neighbors: ");
Node node;
Iterator<Node> iterator = list.iterator();
while (iterator.hasNext()) {
node = iterator.next();
if (!node.equals(this))
System.out.print(node + "[" + node.getId() + "] ");
}
} else {
System.out.print(this + " has no neighbors");
}
System.out.println();
}
/* this method has no implementation in this class (hence its being abstract); each
* subclass MUST implement it (or their subclasses!), allowing differing algorithms.
* the signature (method name and parameter list) must be identical for every subclass
*/
public abstract int doSomeNodeBehavior();
}
/* this class knows and does everything a Node knows and does, and adds a bit more; it
* can do additional things differently or other than what EchoNode does
*/
class WaveNode extends Node {
public WaveNode(int id) {
super(id);
}
public void doWaveBehavior() {
// do something wavy here
}
public int doSomeNodeBehavior() {
// do the wave algorithm
return 0;
}
}
/* this class knows and does everything a Node knows and does, and adds a bit more
* can do additional things differently or other than what WaveNode does
*/
class EchoNode extends Node {
public EchoNode(int id) {
super(id);
}
public void doEchoBehavior() {
// do something echoy here
}
public int doSomeNodeBehavior() {
// do the echo algorithm
return 0;
}
}
/* it is best to reduce the amount of behavior the Node container (ArrayList in this case)
* does beyond what is typical for an Abstract Data Type (ADT) element; make the additional
* behavior in other classes. visit each node to perform specific behaviors and let the
* polymorphic behavior determine exactly what to do. Note: subclass specific behavior is
* not possible without downcasting, and that MAY be a sign of poor design
*/
public class Nodes {
public static void main(String[] args) {
List<Node> list = new ArrayList<Node>();
list.add(new WaveNode(1));
list.add(new WaveNode(2));
Node node = new EchoNode(1);
list.add(node);
list.add(new EchoNode(2));
node.printNeighboursOf(list);
}
}
Upvotes: 0
Reputation: 17
Use generics to do this instead of typing specifically on Node. Change the return signature from Set<Node>
to Set<? extends Node>
or have the subclass handle the generic type rather than its type.
Upvotes: 1
Reputation: 4197
Set<WaveNode>
or Set<EchoNode>
is not a subclass of Set<Node>
and can't be casted to it; and you can't invoke remove(WaveNode ..)
with argument of superclass type (Node
). If you would have keySet
as Set<Node>
it would be ok, or use raw type: Set nei = rec.keySet();
Upvotes: 0