Anonymous class extends NSObject

Is there possibility to extend NSObject without writing a lot of boilerplate?

For example, I need override UIViewController method shouldAutorotate. Ordinary java approach is anonymous class:

class A {
  UIViewController c;
  boolean autorotate = true;
  void a() {
    c = new UIViewController() {
      @Override
      public boolean shouldAutorotate() {
        return autorotate;
      }
    };
  }
}

What should I write in MOE? Something like this:

static class MyController extends UIViewController {

    private B b;

    protected MyController(Pointer peer) {
      super(peer);
    }

    public void setB(B b) {
      this.b = b;
    }

    @Selector("alloc")
    public static native MyController alloc();

    @Selector("init")
    public native MyController init();

    @Override
    public boolean shouldAutorotate() {
      return b.autorotate;
    }
    }

      class B {
        MyController c;
        final boolean autorotate = true;
        void a() {
          c = MyController.alloc().init();
          c.setB(this);
        }
      }

Much more code for simple operation. And I cannot use final fields in my MyController class.

Dear Zufar,

There are 2 sides to your question:

  1. Yes, the Nat/J bindings follow the Objective-C way to construct instances, and this prohibits the creation of such “anonymous” subclasses. Technically, it would be possible to support both options, e.g. for every method starting with init*() we could generate a constructor with the same parameters, that would call the correct alloc().init*() sequence on the Objective-C side behind the scenes, and only store the result from the init*() method to handle Objective-C class clusters correctly. We would be happy to implement this feature in a future release, the question is, how important is this feature to the community?

  2. In my experience using anonymous classes to extend other classes in Java is an antipattern: in my experience such designs tend to be hard to maintain, and get messy over time. In our designs we try to restrict the use of anonymous classes to “closure” / “lambda function” like use cases, when an interface is implemented in such manner. These use cases work with the current Nat/J implementation, for example, in the following sample:

@Runtime(ObjCRuntime.class)
@ObjCProtocolName("CounterService")
public interface ICounterService {

    @Selector("increment")
    int increment();

    @Selector("getCount")
    int getCount();

}

@ObjCClassName("CounterService")
@RegisterOnStartup
public class CounterService extends NSObject {

    protected CounterService(Pointer peer) {
        super(peer);
    }

    private static ICounterService INSTANCE = new ICounterService() {

        private int counter;

        @Override
        public int increment() {
            return ++counter;
        }

        @Override
        public int getCount() {
            return counter;
        }
    };

    @Selector("get")
    public static ICounterService get() {
        return INSTANCE;
    }
}

There are of course exceptions, and there could be APIs where the natural way to go is to create an anonymous subclass of an abstract class, which is currently not possible with Nat/J based bindings. As I said before, we are open to implement this feature, I just added an issue to the backlog for it on GitHub. We will prioritize it based on the needs and interest of the community.

We just updated the mentioned samples for MOE 1.1, you can download them from here: https://github.com/migeran/moe-java-samples
Also, please note the special proguard.append.cfg that we added in the above sample project: ProGuard will remove the methods if they are not used from Java, only from Objective-C. To prevent this, you need to specify the appropriate -keep rules in your ProGuard configuration.

Best Regards,
Gergely

  1. Generating constructors with correct alloc().init() code would be cool. Vote for it!
  2. I need to create anonymous classes for testing purposes mostly. So, I can write more code, de-anonymise these classes, but it’s handy java feature that make my life easier.

Dear Zufar,

thank you for your feedback. I can see how anonymous classes can be useful for testing. We will see how / when we can schedule this feature.

Best Regards,
Gergely

If you are prepared to use the Xtend language (it’s awesome), I wrote some active annotations that do the work for you. For example:

@Alloc @Init
class MyViewController extends UIViewController {

	@Accessors String name

	@NSConstructor
	def create(String name) {
		this.name = name
	}
}

The alloc and init annotations generate the (typed) methods, and the protected pointer constructor. Optionally you can also easily add constructors that call alloc and init for you. So the above class can be instantiated like this:

val myController = MyViewController.create('Hello world')
1 Like