One goal of programming is to manage complexity. A good app isn't simply useful or visually appealing — a good app's code is nicely organized, easy to understand, and easy to modify.
Certain programming languages, like C++, support multiple inheritance, in which a class can have more than one parent class. For example, in C++ you can create a Book
class, a TeachingMaterial
class, and a Textbook
class. You can make Textbook
extend both Book
and TeachingMaterial
. This feature makes class hierarchies quite flexible, but it also makes those same hierarchies extremely complicated. You need tricky rules to decide how to inherit the move methods of both the computer's Mouse
class and the rodent's Mouse
class.
To avoid all this complexity, Java doesn't support multiple inheritance. In Java, each class has one (and only one) superclass. A class can have any number of subclasses. You can (and will) create many subclasses of Android's AppCompatActivity
class. And other developers create their own subclasses of Android's AppCompatActivity
class. But classes don't have multiple personalities. A Java class can have only one parent. The Executive
class cannot extend both the FullTimeEmployee
class and the PartTimeEmployee
class.
The relationship between a class and its subclass is one of inheritance. In many real-life families, a child inherits assets from a parent. That's the way it works.
But consider the relationship between an editor and an author. The editor says, “By signing this contract, you agree to submit a completed manuscript by the fifteenth of August.” Despite any excuses that the author gives before the deadline date, the relationship between the editor and the author is one of obligation. The author agrees to take on certain responsibilities; and, in order to continue being an author, the author must fulfill those responsibilities. (By the way, there's no subtext in this paragraph — none at all.)Now consider Barry Burd. Who? Barry Burd — that guy who writes Java Programming for Android Developers For Dummies, 2nd Edition, and certain other For Dummies books (all from Wiley Publishing). He's a college professor, and he's also an author. You want to mirror this situation in a Java program, but Java doesn't support multiple inheritance. You can't make Barry extend both a Professor
class and an Author
class at the same time.
Fortunately for Barry, Java has interfaces. A class can extend only one parent class, but a class can implement many interfaces. A parent class is a bunch of stuff that a class inherits. On the other hand, as with the relationship between an editor and an author, an interface is a bunch of stuff that a class is obliged to provide.
Here's another example. Though a company might hire consultants, consultants who work for the company aren't employees. Consultants are normally self-employed. They show up temporarily to help companies solve problems and then leave the companies to work elsewhere. In the United States, differentiating between an employee and a consultant is important: So serious are the U.S. tax withholding laws that labeling a consultant an “employee” of any kind would subject the company to considerable legal risk.
To include consultants with employees in your code, you need a Consultant class that’s separate from your existing Employee
class hierarchy. On the other hand, consultants have a lot in common with a company's regular employees. For example, every consultant has a getPayString
method. You want to represent this commonality in your code, so you create an interface. The interface obligates a class to give meaning to the method name getPayString
.
package
com.allyourcode.company;
public interface Payable {
public String getPayString();
}
The element in the code above isn't a class — it's a Java interface. Here’s what the listing's code says:
As an interface, the getPayString
method has a header, but no body. In this interface, the getPayString
method takes no arguments and returns a value of type String
. A class that claims to implement the Payable
interface must provide (either directly or indirectly) a body for the getPayString
method. That is, a class that claims to implement Payable
must, in one way or another, implement the getPayString
method.
The next two sections of code implement the Payable
interface and provide bodies for the getPayString
method.
package com.allyourcode.company;
import java.text.NumberFormat;
import java.util.Locale;
public class Consultant implements Payable {
String name;
double hourlyFee;
int hoursWorked;
static NumberFormat currency = NumberFormat.getCurrencyInstance(Locale.US);
public Consultant() {
}
public Consultant(String name, double hourlyFee, int hoursWorked) {
this.name = name;
this.hourlyFee = hourlyFee;
this.hoursWorked = hoursWorked;
}
public double pay() {
return hourlyFee * hoursWorked;
}
@Override
public String getPayString() {
return name + ", " + currency.format(pay()) + "\n";
}
}
Check out this code: Another Class Implements the Interface
package com.allyourcode.company;
public class Employee implements Payable {
String name;
String jobTitle;
int vacationDays;
double taxWithheld;
public Employee() {
}
public Employee(String name, String jobTitle) {
this.name = name;
this.jobTitle = jobTitle;
}
@Override
public String getPayString() {
return name + ", Pay not known\n";
}
}
Both the Consultant
and Employee
classes implement the Payable
interface — the interface that summarizes what it means to be paid by the company. With this in mind, consider this code:
package com.allyourcode.a10_10;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.allyourcode.company.Consultant;
import com.allyourcode.company.Employee;
import com.allyourcode.company.Payable;
public class MainActivity extends AppCompatActivity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
Employee employee = new Employee("Barry", "Author");
Consultant consultant = new Consultant("Willy", 100.00, 30);
textView.setText("");
displayPay(employee);
displayPay(consultant);
}
void displayPay(Payable payable) {
textView.append(payable.getPayString());
}
}
The displayPay
method doesn't know anything about Employee
classes or Consultant
classes. All the displayPay
method knows is that it wants its parameter to implement the Payable
interface. As long as the object you pass to displayPay
implements the Payable
interface, the displayPay
method's body can safely call the getPayString
method.
Both the Employee
and Consultant
classes implement the Payable
interface. So, you can pass an Employee
object to the displayPay
method, and pass a Consultant
object to the displayPay
method. That flexibility — the ability to pass more than one kind of object to a method — illustrates the power of Java's interfaces.
Two otherwise unrelated classes (Employee
and Consultant
) both implement the Payable
interface.
The dotted line isn't part of standard UML. The folks who manage the standard have much better ways to represent interfaces.