Java is pass-by-value. How could you modify the language to introduce passing by reference (or some equivalent behavior)?
Take for example something like
<
Think about how it might be implemented with a primitive type, say int
. Java - the JVM, not just the language - does not have any "pointer" type to a local variable, on the frame (method stack) or the operand stack. Without that, it is not possible to truly pass by reference.
Other languages that support pass-by-reference use pointers (I believe, though I don't see any other possibility). C++ references (like int&
) are pointers in disguise.
I've thought of creating a new set of classes that extend Number
, containing int
, long
, etc. but not immutable. This could give some of the effect of passing primitives by reference - but they won't be auto-boxed, and some other features might not work.
Without support in the JVM, you can't have real pass-by-reference. Sorry, but that's my understanding.
BTW, there are already several Reference-type classes (like you'd like for Holder). ThreadLocal<>
(which has get()
and set()
), or the Reference
extenders, like WeakReference
(which I think only have get()
).
Edit:
After reading some other answers, I'd suggest that ref
be a form of auto-boxing. Thus:
class ReferenceHolder<T> {
T referrent;
static <T> ReferenceHolder<T> valueOf(T object) {
return new ReferenceHolder<T>(object);
}
ReferenceHolder(T object) { referrent = object; }
T get() { return referrent; }
void set(T value) { referrent = value; }
}
class RefTest {
static void main() {
String s = "Hello";
// This is how it is written...
change(s);
// but the compiler converts it to...
ReferenceHolder<String> $tmp = ReferenceHolder.valueOf(s);
change($tmp);
s = $tmp.get();
}
// This is how it is written...
static void change(ref Object s) {
s = "Goodbye"; // won't work
s = 17; // *Potential ClassCastException, but not here*
}
// but the compiler converts it tothe compiler treats it as:
static <T> void change(ReferenceHolder<T> obj) {
obj.set((T) "Goodbye"); // this works
obj.set((T) 17); // *Compiler can't really catch this*
}
}
But see where there is potential for putting the wrong kind of type in the ReferenceHolder
? If genericized properly, the compiler may be able to warn sometimes, but as you likely want the new code to resemble normal code as much as possible, there is the possibility of a CCEx with each auto-ref call.
To answer your question:
Where can this fail?
this
new
...and possibly others. Basically, your ref
keyword must only be usable if the parameter source is a non-final field or local variable. Any other source should generate a compilation error when used with ref
.
An example of (1):
final String s = "final";
passByReference(ref s); // Should not be possible
An example of (2):
passByReference(ref this); // Definitely impossible
An example of (3):
passByReference(ref toString()); // Definitely impossible
passByReference(ref new String("foo")); // Definitely impossible
An example of (4):
passByReference(ref "literal"); // Definitely impossible
And then there are assignment expressions, which seem to me like something of a judgement call:
String s;
passByReference(ref (s="initial")); // Possible, but does it make sense?
It's also a little strange that your syntax requires the ref
keyword for both the method definition and the method invocation. I think the method definition would be sufficient.
Answering you question about how to extend the language my pick would be: - Using various holders technics as several other answers describe - Use annotations to attach metadata regarding which arguments should be passed by reference and then start juggling with a byte code manipulation library, like cglib in order to fulfil your ideas in byte code itself.
Though this whole idea seems strange.
I think you can accomplish most of what you want by building an agent and using cglib.
Many of the examples given here can work. I'd recommend using the template you proposed because it will compile with the normal compiler.
public void doSomething(@Ref String var)
Then behind the scenes you use cglib to rewrite the annotated methods, which is easy. You'll also have to rewrite the caller, which i think will be much more complicated in cglib. javassist uses more of a "source code" oriented approach, and might be better suited for rewriting the callers.