Just An Application

November 17, 2016

Java To Swift: Much Ado About Null

Filed under: Java, Java2Swift, Swift — Tags: , , — Simon Lewis @ 12:09 pm

The Java literal null is the only value of the null type.

The null type is anonymous, that is to say, there is no way to refer to it in a Java program so it is not possible to declare anything to be of type null.

But not to worry, although nothing can be declared to be of type null, any field, local variable or parameter which is a reference, that is to say, whose type is declared to be an array, class or interface, can have the value null.

The following code shows the things that can be done with a reference in Java

    package xper.nullexample;

    public class Cod 
    {
	public void swim()
	{
	    // TODO
	}
	
	public int numberOfFins;
    }

    public class XperNullExampleMain 
    {
	public static void main(String[] args)
	{
	    test((Cod)null);
	}
	
	private static void test(Cod fish)
	{
	    if (fish != null)
	    {
	        System.out.println("fish is not null");
	    }
	    else
	    {
		System.out.println("fish is null");
	    }
	    if (fish == null)
	    {
		System.out.println("fish is still null");
	    }
	    else
	    {
		System.out.println("fish is not null !");
	    }
	    if (fish instanceof Cod)
	    {
		System.out.println("fish is an instanceof Cod");
	    }
	    else
	    {
		System.out.println("fish is not an instanceof Cod");
	    }
		
	    System.out.println(fish + " concatenated with string");

	    try
	    {
		System.out.format("fish has %d fins\n", fish.numberOfFins);
	    }
	    catch (NullPointerException npe)
	    {
		System.out.println("Caught NPE when accessing numberOfFins field");
	    }
	    try
	    {
		fish.swim();
		System.out.println("fish is swimming");
	    }
	    catch (NullPointerException npe)
	    {
		System.out.println("Caught NPE when invoking swim method");
	    }
        }
    }	

They are

  • field access
  • method invocation
  • string concatenation
  • casting
  • comparison using == and !=
  • type checking using instanceof

As the example also shows, if you run it, all but two of these work even when the reference is null

The two that do not work are

  • field access

and

  • method invocation

Attempting to use a reference with the value null to access a field or invoke a method or will result in a NullPointerException (NPE).

There is no null in Swift.

The nearest thing is nil but in some respects nil is the anti-null.

In Java the field declaration

    private java.lang.Character c;

means that c can be a reference to an object of type java.lang.Character or null.

In Swift the declaration

    private c : java_lang_Character

means that c cannot be nil.

So whereas in Java the default is that c can be null, in Swift the default is that c cannot be nil.

In Swift an ‘equivalent’ to the Java declaration would be

    private c : java_lang_Character?

The property c must be explicitly declared optional.

To invoke a method on c in Java we can simply do

    c.compareTo(d)

and if c has the value null we get an NPE.

There are two ways of doing the same thing in Swift.

Either

    c?.compareTo(d)

or

    c!.compareTo(d)

The first is optional-chaining, the second is forced unwrapping.

Optional chaining results in the value nil if something in the chain is nil or an optional value of the same type as the last thing in the chain.

Optional chaining works by stopping the first time it encounters nil.

If c is nil in the first example then nothing happens. No attempt is made to invoke the compareTo method.

Forced unwrapping takes a more robust approach.

If c is not nil then the result is a value of type java_lang_Character. If c is nil then the program goes bang.

To preserve the runtime behaviour of Java in Swift when using nil as null is obviously going to require some additional effort.

This is the definition of the compareTo method for java.lang.Character

    public int compareTo(Character anotherCharacter) {
        return compare(this.value, anotherCharacter.value);
    }

The value field is defined like this

    private final char value;

and the compare method is defined like this

    public static int compare(char x, char y) {
        return x - y;
    }

Since anotherCharacter can legitimately be null in Java, the equivalent in Swift would have to be something like

    public func compareTo(anotherCharacter:java_lang_Character?)
    {
        return java_lang_Character.compare(self.value, anotherCharacter?.value)
    }

Assuming the compare method is defined something like this in Swift

    public func compare(x:JavaChar, y:JavaChar) -> Int
    {
        return x - y
    }

and the field value like this

 private let value : JavaChar

then compareTo won’t compile.

The result of the optional chaining on anotherChar is going to be an optional JavaChar

    JavaChar?

To make it compile it is necessary to unwrap anotherCharacter.

One way to do this is like so

    public func compareTo(anotherCharacter:java_lang_Character?)
    {
        return java_lang_Character.compare(self.value, anotherCharacter!.value)
    }

This now compiles but if anotherCharacter is nil then the program stops abruptly.

To preserve the Java runtime semantics it should throw a NullPointerException.

One way to achieve this by defining a ‘magic’ function which checks whether is something is nil, throws an NPE if it is, or returns the unwrapped value if it is not.

The signature needs to be something like this.

    func JRTEnsureNotNull(reference:R?) throws -> R

Assuming such a thing existed it would be possible to define the compareTo ‘method’ like this

    func compareTo(anotherCharacter:java_lang_Character?) throws
    {
        return java_lang_Character.compare(self.value, try JRTEnsureNotNull(anotherCharacter).value)
    }

Because the function JRTEnsureNotNull is declared to throw the call to it must be prefaced by try and since there is no attempt to catch whatever is thrown the ‘method’ itself must be declared to throw as well.

Clearly there is an overhead associated with doing this but the same overhead must exist in some form in the Java runtime since it can detect when a reference is null and throw a NullPointerException if necessary.

In addition, it should be possible in some circumstances to reduce this overhead during the conversion from Java to Swift by identifying when a reference has successfully been dereferenced and using it directly on subsequent occasions rather than checking it each time.

Advertisements

Blog at WordPress.com.

%d bloggers like this: