These are rarely used in .NET anyway so you can’t really complain of their lack in Java. Furthermore, some of their legitimate uses, such as the static TryParse
methods on value types are not applicable in Java. Why? Because in Java, the primitives (int
, long
, byte
, etc. – the equivalent of the basic value types in .NET) are not part of the type hierarchy and they have reference-type wrappers (Integer
, etc.) which would solve the issue of returning the result in case of ‘TryParse
’ style of parsing. How’s that? It’s like:
public static Integer tryParseInt(String intString) {
try {
return Integer.parseInt(intString);
} catch (NumberFormatException e) {
return null;
}
}
No need for an ‘out
’ parameter or ‘ref
’. But! Let’s try and simulate ‘ref
’ using a generic class written this way:
public class Ref<T> {
private T value;
public Ref() {
}
public Ref(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
@Override
public String toString() {
return value == null ? null : value.toString();
}
}
Now, suppose we have a class PhoneNumber
that expects a number as [firstPart]-[secondPart] (both being integer, positive numbers). This class could look like:
public class PhoneNumber {
private final int firstPart;
private final int secondPart;
private final static int NOT_FOUND = -1;
public PhoneNumber(int firstPart, int secondPart) {
this.firstPart = firstPart;
this.secondPart = secondPart;
}
public int getFirstPart() {
return firstPart;
}
public int getSecondPart() {
return secondPart;
}
@Override
public String toString() {
return Integer.toString(firstPart) + "-" + Integer.toString(secondPart);
}
}
We will also suppose that another allowed string
representation of a phone number would be “private
” (irrespective of the casing). We want to create a static tryParse
method on that class. If the given string
is not a valid phone number, we could return null
. But in case of the “private
” value, we would also be forced to return null
. There would be an ambiguity. Therefore, we would need some kind of disambiguation. One way would be to use the Ref<t>
class defined above like so:
public static boolean tryParse(String phoneNumberString, Ref<PhoneNumber> phoneNumber) {
if (phoneNumberString == null || phoneNumberString.isEmpty()) return false;
if (phoneNumber == null) throw new IllegalArgumentException();
phoneNumber.setValue(null);
if (phoneNumberString.toLowerCase().equals("private")) {
return true;
}
int dashIndex = phoneNumberString.indexOf('-');
if (dashIndex == NOT_FOUND || dashIndex == 0 || dashIndex == phoneNumberString.length() - 1) {
return false;
}
String firstPartString = phoneNumberString.substring(0, dashIndex);
String secondPartString = phoneNumberString.substring(dashIndex + 1, phoneNumberString.length());
int firstPart;
int secondPart;
try {
firstPart = Integer.parseInt(firstPartString);
secondPart = Integer.parseInt(secondPartString);
} catch (NumberFormatException e) {
return false;
}
phoneNumber.setValue(new PhoneNumber(firstPart, secondPart));
return true;
}
An example of using that code could be:
public static void main(String[] args) {
String s1 = "555-1234";
String s2 = "555";
String s3 = "saldkfjasdf";
String s4 = "PRIVATE";
String s5 = "";
String s6 = null;
Ref<PhoneNumber> pn1 = new Ref<PhoneNumber>();
Ref<PhoneNumber> pn2 = new Ref<PhoneNumber>();
Ref<PhoneNumber> pn3 = new Ref<PhoneNumber>();
Ref<PhoneNumber> pn4 = new Ref<PhoneNumber>();
Ref<PhoneNumber> pn5 = new Ref<PhoneNumber>();
Ref<PhoneNumber> pn6 = new Ref<PhoneNumber>();
boolean b1 = PhoneNumber.tryParse(s1, pn1);
boolean b2 = PhoneNumber.tryParse(s2, pn2);
boolean b3 = PhoneNumber.tryParse(s3, pn3);
boolean b4 = PhoneNumber.tryParse(s4, pn4);
boolean b5 = PhoneNumber.tryParse(s5, pn5);
boolean b6 = PhoneNumber.tryParse(s6, pn6);
System.out.println("Parsing '" +
s1 + "' : " + b1 + ", Ph# : " + pn1);
System.out.println("Parsing '" +
s2 + "' : " + b2 + ", Ph# : " + pn2);
System.out.println("Parsing '" +
s3 + "' : " + b3 + ", Ph# : " + pn3);
System.out.println("Parsing '" +
s4 + "' : " + b4 + ", Ph# : " + pn4);
System.out.println("Parsing '" +
s5 + "' : " + b5 + ", Ph# : " + pn5);
System.out.println("Parsing '" +
s6 + "' : " + b6 + ", Ph# : " + pn6);
}
Upon running this piece of code, we would receive:
Parsing '555-1234' : true, Ph# : 555-1234
Parsing '555' : false, Ph# : null
Parsing 'saldkfjasdf' : false, Ph# : null
Parsing 'PRIVATE' : true, Ph# : null
Parsing '' : false, Ph# : null
Parsing 'null' : false, Ph# : null
This way, we were able to simulate a ref
parameter. However, some might argue that this is not the only solution and we could have defined another class called ParseResult<t>
encapsulating a boolean representing the success of parsing and a T
representing the parsed value (in case the parsing succeeded). Of course, we could do this, it would be a good alternative. However, for the C# / .NET developers, the Ref<t>
would be closer to their existing knowledge.
Regarding the out
parameter, things are not so bright. That’s because of two things: first, the called method should be enforced to set at least once a value to the out
parameter on each execution path and in C#, this is enforced by the compiler while in Java, I see no way to enforce it. Second, the solution (if one would exist) should restrict the called method to only set the value but not read it while the method caller should be able to read and write.
In conclusion, at least we can simulate ref
with Ref<t>
. This pattern is also used in EventArgs
and other derived classes, in .NET.