Skip to main content

For API Nerds: Interfaces and Inner Classes

This article is for API developers only. If you're writing an application, you may not care about APIs, because you probably don't have any. But if you're writing a library that others will use, public API comes into play.



API is like a pile of laundry: you can either spread the huge, reeking mess out all over your floor, or you can stuff it into a nice, clean hamper. While the first approach provides the convenience of being able to select each day's pre-soiled attire quickly, since it is arrayed out in front of you when you get out of bed, the second solution presents a clean interface to people that make the mistake of walking into your pathetic, slovenly life.



Good APIs prefer hampers.



I was implementing a new piece of API and functionality and wondered the following: Is there any reason to not put my implementation (which I did not want in the public API, and which would be the only implementation of said interface) inside the interface itself? It seems odd, perhaps, maybe even a bit tawdry, but is there anything wrong with it?



My motivation was simple: I'm adding this API and implementation into a package that already has many classes to wade through. Should I bother adding more noise to the list for an implementation detail of this single interface? Why not put it into the interface file, and just bundle up that implementation in a logical place, tightly bound to the interface that it implements?



So I did this, coming up with something like the following:



public interface A {
void a();

static class AImpl implements A {

@Override
public void a() {
// ...
}
}
}



Additionally, an existing class exposed a single method giving a reference to this interface:



public A getA() {
return new AImpl();
}



This worked well - the users of the public getA() method got what they needed: an object of type A with its spectacular, if slightly under-documented, method a(). And I successfully hid the implementation class inside of this same file, as a package-private static class, saving my package the unbearable burden of yet another file.



Done!



Then I ran JavaDocs on my project and realized my mistake: my supposedly package-private implementation class was now part of the public API, showing up as the public class A.AImpl. What th-... I didn't say it was public! In fact, I explicitly made it package-private, so that the class exposing the new getA() method could instantiate an instance of that class. So what happened?



Interfaces happened. Interfaces do not use the same access rules as classes. Instead, all members of an interface are public by default. So while I used the correct (to my mind) syntax for package-private access, I was actually using the correct (to the mind of my interface) syntax for declaring that inner class public, and the JavaDocs did the rest.



Do I hear you yelling, "You could make it private!?" Or is that just the echo of my internal shouting when I first saw the problem? This is what I tried to do. This fails for (at least) two reasons. One is that I actually needed this class to be package-private (not private), so that I could instantiate it and override it from outside of this interface. An even better reason is that you cannot declare different access permissions than the default that the interface uses. In this case, that means that I cannot have a public interface with a private inner class. I could declare the entire interface to be private... but that defeats the whole thing I was going for by exposing the interface A as a public API.



There are other ways around this. For example, there is a mechanism we use in Android framework code, @hide, to solve the problem of having to expose API internally but not wanting it to be a part of the public API (a workaround for the language not having the ability to differentiate between internal and external access to library APIs). But at the point where I considered using this workaround, the awkwardness of putting the class inside of the interface just got to be too much.



In the end, I just pulled the class out and put it at the same level as A. It added another file to the package, but that really wasn't that big a deal anyway. And it was certainly better than the mess I was creating with my class-inside-interface approach.



The moral of the story is: API design is tricky. Consider not only the internal implementation details ("Can I make my life easier by implementing the solution in this particular way?"), but also (and wayyyyy more importantly), "Can I make the API, and therefore the lives of external developers, better by doing it in this other way?"



Here is a technical diagram, illustrating the problem and the original solution:











Comments

Popular posts from this blog

Women and children overboard

It's the  Catch-22  of clinical trials: to protect pregnant women and children from the risks of untested drugs....we don't test drugs adequately for them. In the last few decades , we've been more concerned about the harms of research than of inadequately tested treatments for everyone, in fact. But for "vulnerable populations,"  like pregnant women and children, the default was to exclude them. And just in case any women might be, or might become, pregnant, it was often easier just to exclude us all from trials. It got so bad, that by the late 1990s, the FDA realized regulations and more for pregnant women - and women generally - had to change. The NIH (National Institutes of Health) took action too. And so few drugs had enough safety and efficacy information for children that, even in official circles, children were being called "therapeutic orphans."  Action began on that, too. There is still a long way to go. But this month there was a sign that ...

Benefits Of Healthy eating Turmeric every day for the body

One teaspoon of turmeric a day to prevent inflammation, accumulation of toxins, pain, and the outbreak of cancer.  Yes, turmeric has been known since 2.5 centuries ago in India, as a plant anti-inflammatory / inflammatory, anti-bacterial, and also have a good detox properties, now proven to prevent Alzheimer's disease and cancer. Turmeric prevents inflammation:  For people who

Austerity-A Fancy Word for Destitute.

The reason for this post is not for the folks who have been caught in the first wave of personal economic hard reality, but the next wave. Regardless of the optimism espoused by grinning leaders and sycophant press, we are entering the final stage of global economic collapse. It began in 2008 and was forestalled for five years with fudge putty, but the weight of global indebtedness cannot be propped any longer and the final crunch is imminent. Austerity measures herald the final throes.  Indications of coming austerity.   Austerity measures are the final last ditch effort, futile or not! Back in the day many of us old-timers went through periods of "hard-times". In retrospect I realize there is no comparison to yesteryear hard times and today's version. Back then, expectations were never very high for the working class, there were no sophisticated systems or conveniences anyway. In fact the difference between being "set" or not was about having treats or not. Si...