Table of Contents:
- The lead-up
- Deadly Diamond of Death
- How interfaces solve the diamond problem?
- The idea behind interfaces
- The practical use of interfaces while writing software
- To conclude..
- Official Oracle Documentation
The lead-up
When I was learning Java in college, I did not understand the use for interfaces.
You can't define functions inside an interface. You can only declare them. You have to have a class implement the interface to define it's functions and then use them.
That's crazy! Pretty worthless piece of crap I would think. I can just write my classes and their functions normally. Why would I declare them in one place and define in another.
I don't even need to declare them. Just write my functions normally in a class. It's totally redundant.
Confused and curious, I asked my teacher why I would ever require to implement an interface.
Almost immediately, she said that interfaces let you achieve multiple inheritance, while normal classes don't - the textbook answer on the difference between classes and interfaces.
And I was like, so? Even if my class can implement multiple interfaces at once, none of them would contain function definitions.
They would all be pretty useless. I can just drop them all and write my class without implementing them. Be it single or multiple, what advantage do I get by using interfaces?
My teacher did not have an answer to that.
With the lack of a proper answer, me and most (if not all) of my friends resigned ourselves to accept that interfaces are some kind of mysterious artifacts. That, given the right incantation, would help us manipulate the space-time continuum, rip through the fabric of Java ecosystem and use multiple inheritance - the fruit that is wrongly forbidden from us.
Meanwhile, we gave the same answer during our engineering vivas that my teacher gave me. And wrote the same in our written exams.
Something felt off though. And I could never digest this.
It wasn't until later, when I dabbled with Android that I really learnt the purpose of interfaces - as a contract which lets you to enforce that, the classes you work with, exhibit certain behavior.
I still come across people whose primary understanding of interfaces is something that allows multiple inheritance in Java.
And that is so ridiculous - saying that interfaces support multiple inheritance while classes don't, when I ask you what's the difference between the two.
It's like saying that the difference between a dog and a horse is that a dog barks while a horse neighs.
While that is indeed true, you must understand that dogs and horses are fundamentally different animals!
If your understanding of a horse is that of a dog that neighs. Then boy! You're going to have a wild time in a farm!
Deadly Diamond of Death
First off, lets understand why multiple inheritance is not allowed in Java.
Consider there's a class 'Animals'. All animals makeNoise().
We derive two child classes from this class - 'Horses' and 'Dogs'. Both of them override the makeNoise() behavior.
Horses makeNoise() by neighing. Dogs makeNoise() by barking.
Now suppose, for whatever godforsaken reason, a mad scientist decides to mix up the genes of Horses and Dogs together, and make a new species of Animals - Horgs.
When the Horgs makeNoise(), will they bark? Or will they neigh?
This is the infamous diamond problem (aka Deadly Diamond of Death) in object-oriented programming.
Java doesn't support multiple inheritance with classes simply because inheriting multiple classes together is problematic.
If a class inherits multiple other classes, and these classes contain functions with the same signature, there arises an ambiguity - when I call the said function with an instance of the inheriting (derived) class, how should it behave?
Out of the many function definitions coming from multiple inherited classes, which should be considered?
And how do I control which definition is used when?
C++ tries to handle the Diamond problem using virtual inheritance and scope resolution.
I'm not really a C++ person. And I don't understand how virtual inheritance works.
But, from what I understand, it requires prior knowledge that a certain function may get involved in multiple inheritance. And it requires to be specially handled.
Now that feels weird to me. I did not find many articles on the topic. So, maybe not many people use it. But, please do recommend me any good material on the topic if you have any.
How interfaces solve the diamond problem?
They don't.
Like I said, they're not a mechanism to allow multiple inheritance. It's just that since interfaces don't have method definitions, they don't have the diamond problem.
That is why you're allowed to implement multiple interfaces. That's all.
But the notion that, the use of interfaces is to enable multiple inheritance, is wrong and far from the truth.
The idea behind interfaces
Consider you're a king. You have a beautiful daughter who has reached the age for marriage.
A lot of princes' from the neighboring nations are eager to marry your daughter. But, you have some basic criteria in mind that they should fulfill before they could be considered to marry your daughter.
- The Prince should be able to fight. 'cause that's a necessary skill to have for a prince
- The Prince should like reading books. 'cause books contain knowledge. And you'd want your son-in-law to be well-educated and wise.
- The Prince should be able to speak Klingon. 'cause that's the sign of a superior man.
You write these requirements down on a big banner and place it above the castle gate.
If a Prince comes and satisfies all these conditions, the gatekeeper awards him a 'Badass Prince' badge and sends him in.
If, however, the prince doesn't meet these requirements, the gatekeeper of the castle won't even allow him inside.
Now, this is exactly the idea behind interfaces.
The banner with the requirements on it is an interface for a 'Badass Prince'. And by enforcing those requirements on the prospective Prince, you're essentially requiring them to implement the 'Badass Prince' interface.
Thus stating, that if a Prince is not a 'Badass Prince' (as defined by your interface), they're not eligible to marry your daughter.
The practical use of interfaces while writing software
If you know a class implements an interface, then you know that class contains concrete implementations of the methods declared in that interface, and you are guaranteed to be able to invoke these methods safely.
— StackOverflow answer
Often, different teams work together to develop a product. They may work on different features or segments that are integrated together to form the final product.
In such a case, it is obvious that there will be certain inter-dependencies within the teams.
Say for example, we're building an e-commerce website. One team is developing the 'Add-to-cart' feature (the process before checkout). And another team is working on the online-payment system (the process after checkout).
We definitely can't make the online-payments-team wait and idle till the cart-team has finished and tested their feature.
But then, if both the teams start working on their features in parallel, how do we ensure that we'll be able to integrate the online-payments with the cart? In the end, both the features should come together to form the final product, right?
For example, the online-payments team needs the cart to be cleared on successful payment. But, since the cart is not ready yet, how would the payments-team do that? What function should they call? What parameters should they pass to it? What if the cart team doesn't implement such a function at all? And instead, you have to write logic to remove every product from the cart individually?
To mitigate such problems, both the teams must brainstorm about the technical requirements of their own features and realize the inter-dependencies. Once that is done, both the teams can design appropriate interfaces.
For example, the teams decide that on checkout, the cart-system will provide a MyCart object to the payments-system. This MyCart class must implement the Cart interface that has the following methods:
1. void addProduct(Product product)
2. Product removeProduct(integer productId)
3. List<Product> getProductsFromCart()
4. boolean emptyCart()
If object provided by the cart-system does not come from a class that has implemented the Cart interface, then the payments-system will throw an Exception.
if(!(myCart instanceof Cart)){
throw new Exception("Expected an instance of Cart");
}
Due to the principle of abstraction in object oriented programming, the payments-team does not need to know how the emptyCart method works. They just need to know:
- that it exists
- that it is intended to do what they want (clear all products in the cart)
- how to call it (what parameters to pass and what to expect in return).
Out of these, interfaces take care of points 1 and 3. Point 2 is left to the human intellect.
Basically, by asking the cart-team to implement the Cart interface, the payments-team says,
"Hey cart-team,
We are not concerned about the logic and implementation on your side.
But, we require certain functionality to be present in the object that you pass to us. And we require it in a very specific manner (with so and so name, so and so parameters, so and so value returned).
Please implement these exactly as specified so that we can just plug your cart-system into our payment-system. And we can have a seamless integration."
This was just a simple example of when an interface is used. There might be more. But, I hope it helps you to understand what an important role the interfaces play in software development.
If you can think of more examples, please let me know in the comments.
In computing, an interface is a shared boundary across which two or more separate components of a computer system exchange information.
...
An interface is thus a type definition; anywhere an object can be exchanged (for example, in a function or method call) the type of the object to be exchanged can be defined in terms of one of its implemented interfaces or base-classes rather than specifying the specific class.
— Wikipedia
Also, a lot of people relate interfaces with legal contracts. This is because interfaces are based on mutual agreement between two parties; on how one party could use the services of another.
You meet the requirements in the contract. And you shall enjoy our uninterrupted services.
You fall short on the terms of the contract, we shall no longer serve you.
A contract is like a promise between people. It is an understanding, a deal between two or more people or organizations to do certain things.
— Arts + Law article
To conclude..
- Multiple inheritance is problematic.
- Java novices often wrongly associate interfaces with multiple inheritance. That's not what they are for.
- An interface is actually a pretty powerful construct that lets you define the requirements of your software component. It also lets you enforce that other software components, that interact with yours, meet those requirements.
Official Oracle documentation
Oracle has provided some really awesome documentation on interfaces. So, please check out the following links and look around.
Top comments (3)
Great article on interfaces, I like your writing voice and the examples you give for the practical use of interfaces while writing software. It reminds me of the concept of Duck Typing 😄
Thank you!! 😊
Duck typing is like the antithesis of interfaces.
When I first learned about duck-typing, I was so used to the type system of Java and C++, that I just couldn't wrap my head around it.
Now, look at me. I'm a Node.js developer! 😛
Yes! welcome to Javascript 😆