Generated bindings and Java generics

Currently generated bindings are forcing to use explicit casts in the Java code, which produces some cluttering as well as a lot of compiler warnings.

For example:

NSMutableArray a = NSMutableArray.alloc().init();
a.add("String");

I can’t specify type NSMutableArray without explicitly casting return value of .init() method.

This is because init() is declared like this:

public native NSMutableArray<?> init();

I’d suggest to change generated code to this:

public native <T> NSMutableArray<T> init();

So, Java compiler will be able to deduce the return value at the assignment:

NSMutableArray<String> a = NSMutableArray.alloc().init();
a.add("String");

Also, in NSArray:

public static native <_ObjectType> NSArray<?> arrayWithObjects(...)

Should be:

public static native <_ObjectType> NSArray<_ObjectType> arrayWithObjects(...)

and Object… vararg param there should be also _ObjectType.

Same issue with NSMutableDictionary, NSDictionary…

Please let me know if those are doable, in that case I will add a few more examples from standard ios frameworks I bumped at.

It should be doable, we created an issue to track this, please add more examples the you would like us to support:

https://github.com/multi-os-engine/multi-os-engine/issues/86

Thank you Gergely.
Also it seem like generated static alloc() factory method might need to be changed to something like this (all the way up in the object hierarchy):

public static native <T> T alloc();

this is to allow specific typing in the inherited classes. But it is going to be a backward incompatible change, i.e. bindings have to be regenerated.

Though this one might go away if you replace those alloc’s with contructors as it was described in one of the open issues. Not sure what’s the timeframe for that.

This is also from one of MOE examples:

The NSLayoutConstraint.constraintsWithVisualFormatOptionsMetricsViews() method is declared like this:

public static native NSArray<? extends NSLayoutConstraint> constraintsWithVisualFormatOptionsMetricsViews(
        String format, @NUInt long opts, NSDictionary<String, ?> metrics, NSDictionary<String, ?> views);

So, I have to explicitly cast like this:

NSMutableArray<NSLayoutConstraint> constraints = (NSMutableArray<NSLayoutConstraint>) NSMutableArray.alloc().init();

NSArray<NSLayoutConstraint> cons1 = (NSArray<NSLayoutConstraint>) NSLayoutConstraint.constraintsWithVisualFormatOptionsMetricsViews( //
			"H:|-12-[tabs]-12-|", 0, null, views);
constraints.addObjectsFromArray(cons1);

I think changing above method declaration to this would help:

public static native NSArray<NSLayoutConstraint> constraintsWithVisualFormatOptionsMetricsViews(
        String format, @NUInt long opts, NSDictionary<String, ?> metrics, NSDictionary<String, ?> views);

Foundation.CFBridgingRelease() declared like this:

public static native Object CFBridgingRelease(ConstVoidPtr X);

Please update to:

public static native <T> T CFBridgingRelease(ConstVoidPtr X);

More detailed explaination on this, just for record:

There could be method clashing if generic type parameter is added to certain methods, for example:

  • in NSObject we have
public static native NSObject alloc();

and in NSArray we currently have

public static native NSArray<?> alloc();

which I know is not ideal, but if we change this to

public static native <ObjectType> NSArray<ObjectType> alloc();

then we will get a compile error:

‘alloc()’ in ‘apple.foundation.NSArray’ clashes with ‘alloc()’ in ‘apple.NSObject’; both methods have same erasure, yet neither hides the other

because the method in NSObject does not have the type parameter. If we want to keep the type parameter in NSArray, we have to change the one in NSObject to

public static native <ObjectType> NSObject alloc();

and this will also be applied to all other non-generic subclasses of NSObject.

This is a limitation of Java’s type erasure system, and I don’t know how we could solve this elegently. With out add generic to alloc(), we cannot have a chainned call like

NSMutableArray<String> a = NSMutableArray.alloc().init();

even if we have

public native NSArray<_ObjectType> init();

Also even if we add a type parameter to NSObject.alloc(), it will still clash with classes that have more than one type parameters, such as NSDictionary.alloc(). So it’s not feasible to make those methods generic.

But also as you’ve metioned

this will indeed solve the problem, as the constructors will hide those type casting.