Sneak Peek: Inline Interface Implementations in Prism XE2 and Cooper
Today, I would like to talk about another new language feature that is new in Oxygene 5. This particular feature was mainly added for Project “Cooper” to fill a need on the Java platform, but it is also available on .NET. The feature is Inline Interface Implementations
Java does not use multicast events like .NET, but instead employs the more traditional delegate model. Classes that fire events or have callbacks usually come with a companion delegate interface that defines any methods that the class wants to call back on. In fact, our event keyword is not supported when compiling for the Java platform.
Of course, implementing an interface in Oxygene is easy, but with the nature of events and callbacks, you might not always want to implement the delegate interface on your main object. For example, what if your form holds two buttons? You can’t really define a global OnClick method on your form and have it handle clicks from either button. That’d be silly. Of course you create a separate helper object, but that’s clumsy and a lot of work.
The Java language allows interfaces to be implemented inline, and we brought that feature over to Oxygene, for all platforms.
So how might this work? Let’s have a look at the following (fictitious) type:
type IButtonDelegate = interface method OnCLick(aButton: Button); method OnGetHoverHint(aButton: Button): String; end; Button = class private public property Caption: String; property Events: IButtonDelegate; end;
We have a button class, along with a delegate interface that defines its events. Whenever the button is clicked or needs to show a hint, it will check if something has been assigned to the Events property, and if yes calls the necessary method.
Now, let’s see how we would go and implement this using the new inline interfaces feature:
begin var b := new Button; var lCaptionWhenClicked := 'You clicked me!'; b.Events := new interface IButtonDelegate(OnClick := method(b2: Button) begin b2.Caption := lCaptionWhenClicked; end, OnGetHoverHint := b2 -> 'This is the '+b2.Caption+' button'); end.
First, you see we are using the new new interface syntax, which works symmetrical to new class for anonymous classes. In essence, this does create an anonymous class, one that implements the IButtonDelegate interface. Inside the parenthesis, we provide a implementation for some or all of the methods that are needed by the interface. These can be in anonymous method format (as OnClick shows in the example) or as lambdas (as OnGetHoverHint illustrates). We could also use a reference to an existing method, using the @ operator.
There are two things of note worth mentioning here: For one, just like regular anonymous methods, our interface implementations have full access to the surrounding code. Our event handlers can access the local lCaptionWhenClicked variable; they could also access self or any members of the containing object. For another, while in this case we implement all (whopping two) members of the interface, that is not necessary. You only need to implement the members you need, empty implementations will automatically be provided by the complier for any method you skip.
As a special convenience, the compiler will treat interfaces with a single method as assignment compatible with regular single method pointers. So if you encounter a delegate interface with only a single method, you can skip the whole “new interface” syntax and simply assign a method pointer, just as you would with a regular .NET delegate:
c.Events := method begin … end;
So that’s inline interface implementations. The feature will be incredibly useful when using Project “Cooper” and working with Java-style frameworks and APIs, but we can also see it coming in handy on the .NET/Mono platform in many cases – not necessarily as a replacement for multi-cast events, but in more traditional “delegate” scenarios.