A few years ago (2011 to be precise) someone asked a question on StackOverflow about support for anonymous classes in Delphi. The reason for the question was that the poster was trying to use Delphi to develop for Android and on that platform the widespread use of callback interfaces makes anonymous classes highly desirable.
As far as I know, despite oft repeated claims about how the Delphi language is “advancing”, Delphi still lacks this useful language feature. Whilst there may be complex and contorted ways to contrive anonymity (as illustrated in the answer to that StackOverflow question) it is much more convenient to have the language support the ability to conjure an anonymous class as and when needed without having to provide all that scaffolding (or to be precise: to have the compiler take care of the scaffolding for you behind the scenes).
Needless to say, Oxygene does this with aplomb (and did so even back in 2011).
Let’s look at a very simple example: Connecting to a GoogleApiClient.
As the hub for a wide range of Google Api’s this is a common task and connecting to the Api client involves handling a number of different callbacks. All of these callbacks are provided to the Api client by supplying implementations of those callbacks as specified in interfaces.
In our example scenario we have an activity which is connecting to the Api client (among other things) and it seems obvious at first to simply declare and implement the required callback interfaces directly on the activity itself:
MainActivity = public class(AppCompatActivity, View.OnClickListener, OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener, ServiceConnection) private fGoogleApis: GoogleApiClient; public method onCreate(savedInstanceState: Bundle); override; // .. etc .. public // -- Interfaces // GoogleApiClient.ConnectionCallbacks method onConnected(theHint: Bundle); method onConnectionSuspended(theCause: Integer); // GoogleApiClient.OnConnectionFailedListener method onConnectionFailed(theResult: ConnectionResult); end;
This activity is not only providing callbacks for the Google Api Client but also for other things such as Google Maps notifications, Location Services updates, UI View click responses and even Service bindings.
The broader details of all of these interfaces aren’t important, neither are the more general details of the MainActivity in this case. What is important is that as we add more capabilities to our activity we can see how the number of interfaces being implemented can increase quite rapidly and, although it’s not shown, this also can result in a large number of methods on the activity, simply to satisfy those interfaces.
In many cases the response to a particular callback is local to the point at which the callback is provided or simply provides a “hook” to respond to a callback to invoke more complex processing in our activity that is also invoked in other situations.
The result: A great deal of noise in our activity, ‘polluting’ the class and making it difficult to separate the real function of the activity from the scaffolding.
Anonymous classes help us address that by avoiding the need to encumber the activity with these details.
First, let’s back up a little and look at how we register the Api client callback interfaces when we configure our client, typically in the onCreate method of our activity:
method MainActivity.onCreate(savedInstanceState: Bundle); begin inherited onCreate(savedInstanceState); // .. fGoogleApis := new GoogleApiClient.Builder(self) .addConnectionCallbacks(self) .addOnConnectionFailedListener(self) .addApi(Auth.GOOGLE_SIGN_IN_API, gso) .addApi(LocationServices.API) .build; // .. end;
We use a GoogleApiClient builder to setup our Api client and register the MainActivity (self) as the provider of the connection callbacks.
There are two separate callback interfaces involved here, but we’ll focus on the first (addConnectionCallbacks())as this is the more complex of the two, requiring the implementation of two methods as opposed to just one.
An Anonmyous Class to Implement a Callback Interface
Instead of providing a reference to the MainActivity as the implementer of the GoogleApiClient.ConnectionCallbacks interface, we can provide an inline implementation of that interface without having to declare a formal class but using an anonymous class instead.
The first step is to instantiate an anonymous class implementing the required interface. We will use this in place of the reference to self when we registered the callbacks with addConnectionCallbacks.
To instantiate an anonymous class we invoke new with the interface to identify the interface to be implemented on the anonymous class:
.addConnectionCallbacks(new interface GoogleApiClient.ConnectionCallbacks());
But of course we also need to provide an implementation for the interface.
To do that we identify the implementation associated with each function by assigning a method body to the required interface member:
.addConnectionCallbacks ( new interface GoogleApiClient.ConnectionCallbacks ( onConnected := method(hint: Bundle) begin // What we do when we're connected to the Api client end, onConnectionSuspended := method(cause: Integer) begin // What we do when our Api client connection is suspended end; ) )
Obviously the way I have formatted the parenthesis blocks here is a purely personal preference. You might prefer an alternate approach.
This approach of assigning values (in this case method bodies) to members of a class directly in the constructor is not unique to anonymous classes. It is the same pattern that is supported more generally in Oxygene to add in-line initialization of members on a new instance immediately following any required constructor parameters.
In the case of anonymous classes of course there is only the default, parameterless constructor, so the additional initialization consists entirely of member method body assignments.
Cleaning Up the Activity
Having implemented the callbacks in this way we can now dramatically simplify our activity class:
MainActivity = public class(AppCompatActivity, View.OnClickListener, OnMapReadyCallback, LocationListener, ServiceConnection) private fGoogleApis: GoogleApiClient; public method onCreate(savedInstanceState: Bundle); override; // .. etc .. end;
All that scaffolding to take care of implementing a few callbacks is swept away and obviously we could reduce it even further in this case if we took the same approach with those other callback interfaces still present.
Which isn’t to say that you would never choose to implement a callback interface directly on an activity or other formal class type defined for the purpose.
In some cases it might still make most sense to do so for a particular interface. But with anonymous classes in your toolbox this becomes a deliberate choice rather than your only option.