I'm currently writing a simple binary tree that consists of Node<T> Objects.
Node<T> has these properties:
private T value = null;
private Node<T> left = null;
private Node<T> right = null;
Currently there are getters and setters for all three.
My question is:
If I add an enum that specifies which side/value of a node i want to access, like:
public static enum Sides {LEFT, RIGHT, VALUE};
Would it be possible to define a generic getter and setter?
I already tried something like this:
public <E> get(Side side) {
switch(side){
case LEFT:
return (E)this.left;
[...]
}
}
Is this the only way to do it, or are there better ones?
And how would I define the setter?
Thanks in advance! :)
If I didn't want to have separate getters and setters for each side (in reality I probably would), and I did want mutability (in reality I probably wouldn't), I'd do
public enum Sides {LEFT, RIGHT}; // FYI static is redundant for inner enums
public Node<T> get(Side side) {
switch(side) {
...
}
}
public void set(Side side, Node<T> node) {
switch(side) {
...
}
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
You are deciding at runtime depending on the current SIDE what type will be returned. Generics are used for type check safety at compile time, not at runtime.
You have two options.
Using Object (without generics)
Create an holder object that can hold Node and non-Node types if you want to use generics. And return the generic holder object.
Related
I'm trying to write a decoder in Java for a certain encoding format. The encoding format supports 4 types of data. string, integer, list, map. Here a list can contain any supported type as its value. Also, a map can contain any supported type as its value but the key has to be a string. My current approach is to wrap the data with an object and to use it recursively.
public class Node {
// value could be
// ==============
// Integer
// String
// List<Node>
// Map<String, Node>
private final Object value;
private final NodeType type;
}
This approach is fine. However, With this approach, I have to perform a manual type checking at the runtime. To avoid that I tried to use Java generics.
public class Node<T> {
private final T value;
}
Now with this approach, the T's type bound should something like this.
Integer or String or List<T> or Map<String, T>
As far as I know, we cannot create such a type bound in Java. I would like to know the best approach to solve this problem.
You could make Node an interface with four implementations.
public interface Node<T> {
T getValue();
NodeType getType();
}
public class IntegerNode implements Node<Integer> {
...
}
public class StringNode implements Node<String> {
...
}
public class ListNode<T> implements Node<List<T>> {
...
}
public class MapNode<T> implements Node<Map<String, T>> {
...
}
I'm trying to call a constructor for a generic abstract class within a method of said class. The code below shows this:
public abstract class Node<T> {
public Collection <Node<T>> pars;
public Collection<Node<T>> interactors;
private boolean target = false;
private boolean multiple = false;
private T value;
//constructor for a simple node
public Node(T val){
this.value = val;
}
//constructor for a multiple interaction node
public Node(Collection<Node<T>> inter, T val){
this.interactors = inter;
this.value = val;
if (inter.size()>0){
this.multiple = true;
}
}
public void find_inters(){
ArrayList<Collection<T>> multi_interactions = search();
for (int i = 0; i < multi_interactions.size(); i++){
Node<T> a = new Node<T>(multi_interactions.get(i), this.value); <----i get compile error here
}
}
}
but I keep getting an error that I can't instantiate type Node. I want to create a new Node object within the function find_inters() but I can't. Anyone know why/possible solutions?
You've said that Node is abstract because you can't yet implement some of the methods, but the problem is that you want Node<T> a to be a Node object, but you have to decide what a will actually do when you call those methods.
You can have classes that don't have all their methods implemented -- that's how abstract classes work -- but you can't have objects that don't have all their methods implemented, because that object can have that method called, and your program needs to know what to do.
What you can do is write Node<T> a = new MySubNode<T>(...), where MySubNode is a subclass of Node with all the methods filled in. Or you can write Node<T> a = new Node<T>(...) { implementations of methods go here }, which is essentially the same thing, except the implementation is in an anonymous class. But you can't instantiate an abstract class.
Just to add to Louis's answer. If search() is abstract, you can change it to return ArrayList<Node<T>>, so the implementing classes can do something like Node<T> a = new MySubNode<T>(...) (unless search() is used somewhere else that requires ArrayList<Collection<T>>).
This question already has answers here:
Java class with concrete type as parameter
(2 answers)
Closed 7 years ago.
Not sure what is going on here. Seems like an auto-boxing problem but I've been stuck on this for awhile and thought it might benefit me to stop stressing out and get some more experienced hands on this. The assignment is essentially implementing a BST and extending it to an implementation of an AVL and then running performance tests. To simplify things we can stick with using Integer as the generic.
The problem I am having is when comparing two Nodes. Autoboxing is not taking place and the intValue() method is not recognized.
public class BinaryNode<Integer> implements Comparable<Integer>
{
Integer data;
BinaryNode<Integer> leftChild;
BinaryNode<Integer> rightChild;
int height;
BinaryNode(Integer data)
{
this(data, null, null);
}
BinaryNode(Integer data, BinaryNode<Integer> lt, BinaryNode<Integer> rt)
{
this.data = data;
this.leftChild = lt;
this.rightChild = rt;
}
public Integer getData()
{
return this.data;
}
public BinaryNode<Integer> getLeft()
{
return leftChild;
}
public void setLeft(BinaryNode newNode)
{
this.leftChild = newNode;
}
public BinaryNode<Integer> getRight()
{
return rightChild;
}
public void setRight(BinaryNode newNode)
{
this.rightChild = newNode;
}
#Override
public int compareTo(BinaryNode<Integer> otherNode)
{
return this.getData() - otherNode.getData();
}
}
Edit: Thanks for the quick feedback. It was just the sort of interaction I needed to look at this differently and understand the quirky behavior I was encountering. Unfortunately I am bound to make this BinaryNode a generic class but the trick was to swap out all of the with or as the book's conventions prefer to use .
The best solution was to change the BinaryNode<Integer> to BinaryNode<AnyType> and remove the compareTo from this class. Now that I am no longer overshadowing java.lang.Integer I can reliably use the Integer.compareTo method as I originally intended.
For the curious, here is the TreePrinter class that I have to interact with that uses the parameterized BinaryNode class. http://www.cs.sjsu.edu/~mak/CS146/assignments/3/TreePrinter.java
In class BinaryNode<Integer>, Integer is a generic type parameter, not the Integer class.
change
public class BinaryNode<Integer> implements Comparable<Integer>
to
public class BinaryNode implements Comparable<Integer>
And change any appearance of BinaryNode<Integer> to BinaryNode.
If you wanted the BinaryNode class to takes a generic data type, you wouldn't write code specific to Integer data type (for example, return this.getData() - otherNode.getData() will never compile if getData() returns some generic type parameter T).
public class BinaryNode<Integer> implements Comparable<Integer>
Says that you have a new generic type named Integer. This is not the java.lang.Integer. This is why you're having issues, because they are completely different.
As Soritos Delimanolis pointed out, it would be better to just drop the generic type altogether.
I am curious as to how you return a generic data type from a function without casting. It seems that the whole point of generics was to remove the need for casting with collections and those compile-time errors that arose from it.
Yet with a method of say this signature:
public E get( K key )
I cannot return type E without doing a cast from another type, e.g. return (E) "test";
Doesn't this defeat the entire purpose of using generics? What am I missing here. How do I return a variable of type E?
Yes you can do that using generic. here is a sample code:
public class Sample<E,K>{
Map<K, E> map = new HashMap(){{
put("key", "value");
}};
public static void main(String[] args) {
Sample<String,String> sample = new Sample<String,String>();
String val = sample.get("key");
System.out.println(val);
}
public E get( K key ) {
E e = map.get(key);
return e;
}
}
Generics are meant to improve re-utilization of your classes and/or methods when the object class being specified in the "template" has nothing or little to do with the functionality that the class class or the method itself provides.
The best example of this are data structures where your generic types are black boxes. You don't care about them, just aggregate them in some way and what is important is how you index,sort,find,traverse,... this objects.
The case you're proposing (return (E) "test";) is against that principle because you are building one of these objects and for that you need to know its class details.
Imagine you want to provide a class to represent pairs of objects. You don't care at all about the nature of these objects, you just want to provide a way of grouping to objects references. This is a typical case of use of generic types:
public class Pair<T,S> {
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
public S getSecond() {
return second;
}
public void setSecond(S second) {
this.second = second;
}
private T first;
private S second;
}
Pair has nothing to do with first or second beyond aggregating them.
I need to write a tree class in Java where each level has a unique object type. The way it is written below does not take advantage of generics and causes alot of duplicate code. Is there a way to write this with Generics ?
public class NodeB {
private String nodeValue;
//private List<NodeB> childNodes;
// constructors
// getters/setters
}
public class NodeA {
private String value;
private List<NodeB> childNodes;
// constructors
// getters/setters
}
public class Tree {
private String value;
private List<NodeA> childNodes;
// constructors
// tree methods
}
This is simplistic implementation, but enough to give general idea:
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class GenericNode {
public static abstract class AbstractNode<V, N> {
private V value;
private List<N> children;
public AbstractNode(V value, N... children) {
this.value = value;
this.children = children != null ? Arrays.asList(children)
: Collections.<N> emptyList();
}
public V getValue() {
return value;
}
public List<N> getChildren() {
return children;
}
public int getNumberOfChildren() {
return children.size();
}
#Override
public String toString() {
return value.toString() + "->" + children.toString();
}
}
// leaf node type, ignore type of children
public static class NodeB extends AbstractNode<String, Object> {
public NodeB(String value, Object... nodes) {
super(value, nodes);
}
}
// example of typical node in the mid of tree
public static class NodeA extends AbstractNode<String, NodeB> {
public NodeA(String value, NodeB... nodes) {
super(value, nodes);
}
}
// top level node type
public static class Tree extends AbstractNode<String, NodeA> {
public Tree(String value, NodeA... nodes) {
super(value, nodes);
}
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public static <V, N extends AbstractNode> int getNodeCount(
AbstractNode<V, N> node) {
int nodeCount = node.getChildren().size();
for (N child : node.getChildren()) {
nodeCount += getNodeCount(child);
}
return nodeCount;
}
public static void main(String[] args) {
NodeB nodeB1 = new NodeB("Leaf node 1");
NodeB nodeB2 = new NodeB("Leaf node 2");
NodeA nodeA = new NodeA("Node with children", nodeB1, nodeB2);
NodeA emptyNodeA = new NodeA("Empty node");
Tree tree = new Tree("Tree", nodeA, emptyNodeA);
System.out.println(tree);
System.out.println(1 + getNodeCount(tree));
}
}
You could make N and V types implement specific interfaces so it will be possible to call some common operations on values and/or children.
EDIT: updated implementation with recursive method for node count retrieval
All you need is a Pair<A, B>. Example of trees:
Pair<A, Pair<B, C>>
Pair<Pair<A, B>, Pair<C, D>>
Pair<Pair<Pair<A, B>, Pair<C, D>>, Pair<Pair<E, F>, Pair<G, H>>
ps: don't do this. :)
This is an ideal spot for everything inheriting from "Node", but even that is unnecessary.\
What you probably want is a single generic "Node" object that contains references to your different classes (use composition before inheritance).
At that point, each of your different classes probably has something that can be done to them (otherwise why are they all in the same data structure?) Have them implement a common interface with this common functionality. The node class can delegate to this interface, or some other class can extract the class by this interface and act on it.
This would be better than trying to force something to also BE a node--do one simple thing and do it well.
--edit--
I can't really add an example that is relevant to you because you didn't post anything about your scenario.
But let's say that you have these different classes A, B * C. First of all are they related AT ALL aside from all being children of Object? Let's say they all implement interface "Iface". (If not, you can just replace Iface with "Object", but this really implies a bad design.)
Anyway, your "Node" object is just one object--
public class Node {
private List<node> children;
private Iface myObject;
... setters, getters, tree implementation, tree navigation, related garbage...
}
Now this is enough to create your tree. One thing that you might be able to do to make things smoother, have "Node implements Iface" and delegate any calls to it's object. For instance, if Iface contains an eat(Food foodtype) method, your node could implement Iface and have a method:
public void eat(Food foodtype) {
myObject.eat(foodtype);
}
This would make the "Node" class act as though it was the class it contained.
By the way--another relatively good idea at this point would be to make myObject "private final" and ensure it is not null in the constructor. That way you would always know it was set and none of your delegated members would have to do null checks.
I don't think generics are going to help you much in this case. Instead of having a different class for each level in the tree. What about one node class that has children and store a different class on each level. That should help eliminate a lot of the duplication.
I'm fairly new to Java, so this might have issues I'm not aware of, but it seems to work on a simple level at least.
Define your main Node class - this one will be the root of the tree.
public class NodeA {
private String _value;
private ArrayList<NodeA> _children;
private int _depth;
public NodeA (String value, int depth) {
_value = value;
_children = new ArrayList<NodeA>();
_depth = depth;
}
//probably want getters for _children and _value here
//this makes a new child, with its type depending on the depth of the tree it will
//be placed at. NodeB and NodeC will both inherit from NodeA
public void add(String value) {
switch (_depth) {
case 0:
_children.add(new NodeB(value, _depth+1));
break;
case 1:
_children.add(new NodeC(value, _depth+1));
break;
}
}
The add() method is going to create a new child for the node using the specified value. If you initialize the root of the tree as a NodeA with depth 0, you can add children to nodes and the tree should end up populated so that the next level contains all NodeB's, and the next all NodeC's. The code for NodeB and NodeC is super simple and could be replicated to create an arbitrary amount of Node levels (here is that code).
public class NodeB extends NodeA {
public NodeB(String value, int depth) {
super(value, depth);
}
//nothing else needed!
The code for NodeC is identical, except for the obvious replacements of B's with C's.
Hope this helps / is the kind of answer you wanted!