Pretending there are pointers in Java
Values. References. Value of a reference? Reference to a value? Value of a value? Reference to a reference? Pass by value, or pass by reference? Pass reference by value, or pass value by reference? Am I making sense? I have tried my hardest not to.
Java is a pass by value language. You can not operate on references in Java. This means that there is no way to change the value of a variable you pass to a method from within that method. At least, not until you read the docs on generic types:
public class Box<T> {
private T t;
public Box(T t) {
set(t);
}
public Box() {
this(null);
}
public T get() {
return t;
}
public void set(T t) {
this.t = t;
}
}
And now you can… kind of. It also seems like a public final Box
could
replace the silly getter-setter pattern we normally use to reveal our privates.
More on that later. For now, what kind of useful things can we do with our new
“pseudo-references”?
In C, we can use pointers to pointers to simplify some operations on graph structures. Typically, those graphs are made of a bunch of nodes that store pointers to each other. By storing a pointer to a pointer to a node when traversing graphs of these linked nodes, we can avoid storing a “last” or “next” pointer to a node when inserting or deleting nodes in a graph. Too bad we can’t do something like that in Java…
class Node<T> extends Box<T> {
public final Box<Node<T>> next;
public Node(T t, Node<T> n) {
super(t);
next = new Box<>(n);
}
}
Well, a Node
stores a reference to some type of variable. Box
did the same
exact thing. Now we can get all UML with it and say a Node
is a Box
and
should, therefore, extend
it. That pointer to a pointer in C allows us to
change the value of next
without storing a reference to two nodes, so we can
store a boxed node and do the same thing. Okay, now what?
public class Stack<T> extends Box<Node<T>> {
public void giveFirst(T t) {
set(new Node<>(t,get()));
}
public T takeFirst() {
Node<T> n = get();
if(n == null) return null;
set(n.next.get());
return n.get();
}
}
We make a stack with them, I guess. A stack would normally be a reference to a
reference, here it is a box of a box. No ambiguous pushing or popping; give and
take is easier to grok. Most importantly, no branches or cases in giveFirst
.
Nice. What about a queue?
public class Queue<T> extends Stack<T> {
private Box<Node<T>> last;
@Override
public void giveFirst(T t) {
super.giveFirst(t);
if(get().next.get() == null)
last = get().next;
}
public void giveLast(T t) {
if(get() == null) {
super.giveFirst(t);
last = get().next;
}
else {
last.set(new Node<>(t,null));
last = last.get().next;
}
}
}
Really a dequeue, but there is nothing a stack can do that a queue can’t do, so
why remove functionality when we can extend it? This could probably be a little
bit prettier, but I see no way around the special cases where last is changed
when the first node is added. Notice that last
is not final
; we would have
to make it a Box<Box<Node<T>>
to avoid changing the actual links between
nodes, which seemed a little excessive.
Well that’s that. You don’t get pointer arithmetic, but in the past I have done
something similar with one length arrays. You do get a sane way to add mutable
behaviors to classes, instead of the humiliating
private Foo f; public Foo getFoo(); public void setFoo(Foo f);
pattern we all learn in school. Finally, you get the groundwork for reference
based data type implementations, and a functioning stack and queue, in about
twenty five sloc. I really need a job.
Comments