Nifty - java BYREF passing.
rzwitserloot posted in programming on December 5th, 2006
I have to give Roel credit for this one, he built something like this @ his workplace at some point, but it’s still nifty. Also, it crashes about half the javac compilers out there, but at least on my local version of eclipse this runs fine and produces the expected output (10, 11, 12, 13):
public final class Reference<T> {
public T ref;
private Reference(T initialVal) {
ref = initialVal;
}
public static <T> Reference<T> reference(T initialVal) {
return new Reference(initialVal);
}
public static void main(String[] args) {
Reference r = create(10);
System.out.println(r.ref);
r.ref++; //A
System.out.println(r.ref);
r.ref += 1; //B
System.out.println(r.ref);
r.ref = r.ref + 1; //C
System.out.println(r.ref);
}
}
The crazy stunts you can pull with generics. Depending on your compiler, either this works, or line ‘A’ causes javac to crash completely, B produces a compile error, and C compiles on all versions.
This device is actually useful because it can replace, completely, every instance where ordinarily you’d use the ‘array of size 1′ hack. This occurs in two unrelated situations:
Pass-by-reference
Let’s say I have an int variable in a method. Call it ‘foobar’. I want to call some method, and pass it not the VALUE of this int, but the object, so that the method I call can change it. OO code style cries everytime you try this, but let’s say you -really- want to. You can’t use ‘int’, obviously, because primitives always pass by value. You can try ‘Integer’ instead, but those are immutable - still can’t be changed. The usual solution is to use a new int[1].
From now on you can use a Reference<Integer>:
Reference<Integer> r = reference(25);
call(r);
System.out.println(r.ref); //prints 30.
public void call(Reference<Integer> r) {
r.ref = 30;
}
Modifying parent scope variables from inside an inner class
The following snippet won’t compile, because any variables accessed inside an on-the-fly written class must be ‘final’:
int x = 10;
new Runnable() { public void run() { x = 12;}}
The array-of-size-1 trick works here again, but Reference looks better:
final Reference<Integer> x = reference(10);
new Runnable() { public void run() { x.ref = 12;}}
(Note, with CICE, ‘the final’ would be inferred due to x being used in an inner class).
Some even crazier stuff with this thing after the jump, and a puzzler!
We can flesh out this Reference thing by adding appropriate hashCode, equals, and toString methods. However, before we consider doing that, what would happen if we create a reference that refers to itself? Something like Reference r = reference(null); r.ref = r;?
Well, if we simply link to the contained object for those 3 methods, calling any of them would result in an endless loop. No good. We can easily safe up our code by writing explicit checks for that condition in those methods.
You can download a full version here.
and now for the puzzler: The above code sample:
Reference r = reference(null); r.ref = r;
compiles but causes some generics warnings. Can you construct a version that compiles to the exact same thing without the warnings (and using @SupressWarnings is obviously not allowed!)?
Second puzzler: The code linked has some safety checks inside that prevent endless loops if you refer a reference to itself. Is there a way you can fake it out and generate an endless loop anyway?

December 5th, 2006 at 10:04
Reinier,
The line
if ( o == null ) return true;should be removed. According to the contract of equals: “For any non-null reference valuex,x.equals(null)should returnfalse”The line
if ( ref == this ) return (o instanceof Reference && ((Reference)o).ref == o);breaks the contract ofhashCode. It causes theequalsto returntrueif both this object and the other object are referencing themselves. However, on this line they are never compared against each other. Since the hashcode depends on the hashcode ofref, two references can be “equal” to each other, but have different hashcodes. The contract forhashCodeis: “If two objects are equal according to the equals(Object) method, then calling thehashCodemethod on each of the two objects must produce the same integer result.”December 5th, 2006 at 10:11
Finally, the line
else return ref.equals(o);also breaks the contract ofequals. The contract states: “It is symmetric: for any non-null reference valuesxandy,x.equals(y)should returntrueif and only ify.equals(x)returnstrue.”If I create a
Referenceto aString, callingequalswith the same string will returntrueand calling theequalsof the string with the reference will returnfalse.December 5th, 2006 at 11:41
Hmm. My second remark is invalid. Reinier adjusted
hashCodeaccordingly. This is valid because every reference to itself doesn’t contain any other information.December 5th, 2006 at 15:55
The equals/hashCode contract has been broken before. Due to the mutable nature of References, using them as keys in HashMaps and the like is not something that’ll ever work right. In this case there is no sensible implementation of equals/hashCode available, I think.