Android WTF: Java Interfaces By Example

This article (and accompanying video) is directed to my novice/junior Android brogrammers and siscripters. It’s really important that you understand how to use and create interfaces, but most examples and explanations I had read online, solved erroneous problems (or none at all). I felt as a beginner, that this lead me to pass off interfaces and abstract classes as either being useless, or something far outside my capabilities of understand (I read many, many articles to no avail).

In retrospect, I believe I just needed someone to give me a good example or two of a problem which could be solved by using an interface. In working through this example, I hope to also demonstrate to you the practicality of using abstractions in your projects. I won’t define that word in English (I might another day), and instead encourage you to know it in the code examples.

This article has an accompanying video tutorial, available here:
https://www.youtube.com/watch?v=VCmi0gBxd0E&t=807s

While I strongly believe that studying and using new concepts and tools in the context of working application code is the best way to learn, one of the problems of being a beginner, is that you might not have any experience building applications, and thus may be lacking reference experiences to ground the concept and its usage.

The first few times I tried to understand what an interface was, and what it was for, I was in effect trying to learn something which solves problems that I didn’t understand yet. Since I had no one to guide me (couldn’t afford a degree), I missed out on the benefits of interfaces (and abstraction in general) for my entire first year of building applications; it was pretty ugly, and I couldn’t change anything without other things breaking!

With any luck, the following examples will help you cross that bridge far sooner than I did.

What is an Interface?

“[1] In the Java programming language, an interface is a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods, and nested types. Method bodies exist only for default methods and static methods. [2] Interfaces cannot be instantiated — they can only be implemented by classes or extended by other interfaces.”

Quoted from Oracle.

[1] For now, I’d simply suggest you think of an interface as a class with less details. That’s what it looks like right? That’s essentially what it is. Don’t worry about why we want something with less details to stand in for a real class; that part will come later.

[2] A key difference between an interface and a class, is that an interface cannot be instantiated. The reason for this distinction should also become self evident as you use the abstraction.

Don’t worry if this isn’t sinking in, come back to this part at the end and see if it’s any clearer.

In the following examples, we’re going to imagine that we’re software engineers working on the pre-release of Android 1.0. Obviously that’s a silly thought, but the imagining is actually part of the learning process. While part of this should teach you what an Interface is and how to make one, the most important takeaway is simply learning to know when an interface might be a good approach, among several possible approaches which we will observe.

Example 1: How to implement a bad Solution

Create the following two Classes:

public class ButtonOne {

//By holding the Activity as a reference variable, we can talk to it when button clicks are fired.

private MainActivityOne mainActivityOne;

public void setMainActivityOne(MainActivityOne mainActivityOne){

this.mainActivityOne = mainActivityOne;

}

/**

*Fires when a User Clicks the Button.

* Let’s not worry how, just assume it works.

*/

private void OnClick(){

//we’re basically relaying the fact that the button was clicked, and which one was clicked (by hypothetical Id), so that the Activity can handle user input.

mainActivityOne.onClick(getViewId());

}

//In reality, the View Id is generated at Runtime. At the moment, I don’t care how.

public int getViewId(){

//fake view id

return 123456;

}

}

Followed by:
public class MainActivityOne extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ButtonOne buttonOne = new ButtonOne();

/*

What does “this” mean? In this particular case, “this” refers to the current Instance

of MainActivityOne. If you’re still confused, try passing “MainActivityOne.this” instead

of just “this” in the method parameters. Notice how it still works :).

*/

buttonOne.setMainActivityOne(this);

}

public void onClick(int viewId){

//handle event somehow

}

}

Obviously this code wouldn’t do anything useful when compiled, but there’s a couple things about it that we need to understand. If our goal was is simply to have one class talk to another, there’s not anything intrinsically wrong about this approach. If our requirements never changed, then doing extra work is hard to justify. However, once our goals start to include “changing requirements”, we’ll see how quickly this solution starts to break down.

Notice how we can now say that ButtonOne has an explicit dependency on MainActivityOne. I know I sound like Captain Obvious here, but we know this because ButtonOne says MainActivityOne in its code. This would mean that for each different kind of Activity, we either need to create a specialized Button class for each Activity (read: making a bad solution worse), or handle this some other way.

If you find yourself repeating the same/similar lines of code, across several similar classes, abstraction is in order! Let’s get to it.

Example 2: Changing Requirements

Specifically, we now need to make sure that our solution satisfies the following new “requirements”:

  • We must figure out a way for multiple Views (Buttons, ImageButton, Pickers, etc.) to talk to whatever class contains them.
  • This solution must also work with multiple classes, as we now have Fragments which can also contain/handle Views.

In looking at our previous solution, we know that things must change. Filling our View classes with explicit dependencies is quite obviously a terrible approach at this point, and we’ll end up with similar issues in our Fragments/Activities.

In other words, we need to figure out a solution which isn’t tightly-coupled to specific classes:

  • Some signs of Tightly-Coupled code: Excessive Explicit Dependencies, changing code in one class/unit breaks other classes/units, excessive specialized classes which do similar things.
  • Some signs of Loosely-Coupled code: Dependencies are made explicit/abstract depending on likelihood of changing requriements and what the situation calls for, you can change specific classes/units of code without breaking other classes/units, classes are only as specialized as they need to be and emphasis is put on reducing complexity and repetitive code (this trespasses in the realm of Cohesion, but I’ll save that for another post).

While there’s multiple ways in which we could solve this problem, I feel like the most straight forward and effective solution is to use an interface instead of direct calls to explicit dependencies.

Now, just as you can have a file which contains a single class, we can have a file which contains a single Interface. However, we can also have nested classes/interfaces within the same file as well. I’m just trying to make the point that you don’t need to make a seperate file like this, but you certainly can. I’m sure if you just work with them, you’ll start to see when they should be nested or kept separate; trust your own instincts once you’re more comfortable.

Create the following Interface. If you’re IDE doesn’t have an obvious way to do that, just make a class and change class to interface like so:

//note that it says “interface” instead of class :)

public interface OnWidgetClickListener {

void onClick(int viewId);

/*

Notice how all I’ve done is just pulled the method we originally had in MainActivityOne into

this Interface. How the method is handled is still up to the class that implements this

interface, but we’ve basically made a Contract that requires a class to have a method

which is called onClick, returns void, and must pass a viewId as parameters.

*/

}

Once you get away from all the jargon, interfaces are actually pretty damn straight-forward. There’s nothing mystical going on in this code, it’s basically just a contract which allows two or more classes to communicate without having to know or care who’s on the other end. As long as both classes uphold their end of the contract (i.e. including the methods, supplying the right params, handling return statements appropriately, etc.), we can keep our objects concerned with their own responsibilities/implementation.

Since the utility of doing this may not yet be obvious (it wasn’t to me at first, but hopefully I’m doing a better job teaching you than the sources I first learned from), let’s implement our new solution and see where we’re at from there:

Create the following classes. I’ve chosen to make copies of our original Activity/Button classes, like so:

public class ButtonTwo {

//Replaced Explicit Dependency with Interface!

private OnWidgetClickListener onWidgetClickListener;

/*

After our Activity is created, it will pass itself into the Button with this method.

The key takeaway here, is that we don’t need to have the Explicit Dependency passed in,

as our Button only cares that whatever Class calls this method, implements the interface.

Give that some contemplation :)

*/

public void setOnWidgetClickListener(OnWidgetClickListener onWidgetClickListener) {

this.onWidgetClickListener = onWidgetClickListener;

}

public ButtonTwo(){}

/**

*Fires when a User Clicks the Button.

* Let’s not worry how, just assume it works.

*/

private void OnClick(){

//as long as setOnWidgetClickListener was set (i.e. it isn’t null),

// this method will work the same as before

onWidgetClickListener.onClick(getViewId());

}

//In reality, the View Id is generated at Runtime. At the moment, I don’t care how.

public int getViewId(){

//fake view id

return 123456;

}

}

Followed by:
public class MainActivityTwo extends AppCompatActivity implements OnWidgetClickListener {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ButtonTwo buttonTwo = new ButtonTwo();

//notice how we can still say “this”, even though the method requires an Interface

// to be passed in? That’s because “this” implements the Interface.

buttonTwo.setOnWidgetClickListener(this);

}

//Note that if this method isn’t present, our compiler/IDE will scream at us. That is BY DESIGN!

@Override

public void onClick(int viewId) {

//handle somehow

}

}

Now that we’ve implemented a new solution, let’s think about what it does for us:

  • We no longer have any explicit dependencies in our Button class. This means that our Button is set up to talk to any kind of class, as long as it implements the interface. This is a great example of “loose-coupling” in action.
  • We can also reuse this interface with multiple kinds of Views, as the interface doesn’t have any dependencies to begin with.

There’s plenty more minutia I could get into, but let’s leave that for now and finish satisfying the rest of our project’s requirements.

Example 3: I guess Interfaces are pretty useful sometimes…

public class Fragment extends android.support.v4.app.Fragment implements OnWidgetClickListener {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

ButtonTwo buttonTwo = new ButtonTwo();

buttonTwo.setOnWidgetClickListener(this);

ImageButton imageButton = new ImageButton();

imageButton.setOnWidgetClickListener(this);

}

@Override

public void onClick(int viewId) {

//do something

}

}

Followed by:

public class ImageButton {

private OnWidgetClickListener onWidgetClickListener;

public void setOnWidgetClickListener(OnWidgetClickListener onWidgetClickListener) {

this.onWidgetClickListener = onWidgetClickListener;

}

public ImageButton(){}

private void OnClick(){

onWidgetClickListener.onClick(getViewId());

}

public int getViewId(){

return 123456;

}

}

I think you’ve probably got the picture by now, but here’s a quick recap anyway:

  • Our old solution kind of worked at first, but started to suck really bad once our requirements changed. We don’t always need flexible solutions, but we should try to understand when we might need them.
  • Our new solution can now handle changing requirements quite easily (as we just did). We could cook up all kinds of Views and Managing Classes, but they just need to respect the Contract/Interface in order for things to function properly.

That’s basically it. A few of you may have noticed that we have some repetitive code between our View classes that could also be pulled out, perhaps by creating a “View” parent class for each widget. That’s absolutely true, but I’ll leave that idea for another day.

Thanks for learning.

Written by

Self-taught software developer & student of computer science.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store