|
|
Interface: | a surface forming a common boundary between two things. |
A crap quote, as I'm sure you'll agree :). As you should have realized, there is no multiple inheritance in Java. In Java, multiple inheritance would mean that one class could extend more than another class, giving it multiple super classes. This is not possible in Java. Interfaces provide a solid workaround for multiple inheritance without the overhead involved with multiple inheritance and the added capabilities of using polymorphism with them, allowing you to add multiple identities to your classes.
The source code for an interface must be added to a file with the same name similar to classes. For example:
public interface MyInterface { }
This would be entered into the source file MyInterface.java.
Interfaces can contain two different types of data, static final variables (constants) or method declarations, which are abstract methods without the need for the keyword abstract (interfaces can only contain methods that do not supply a code body anyway, so the need for the keyword abstract is, well, not needed).
public interface GameData { public static final int SCREEN_WIDTH = 640; public static final int SCREEN_HEIGHT = 480; public static final int TOTAL_PLAYERS = 4; }
We use the keyword interface for defining interfaces, just as we have used the keyword class to define our classes, followed by its name/identifier. The interface GameData simply defines three constant variables: SCREEN_WIDTH, SCREEN_HEIGHT, and TOTAL_PLAYERS. Interfaces can also define methods, as follows.
public interface Moveable { public void move(); }
The Moveable interface declares one method, move, which as you can see does not actually implement code for the method itself. Note that an interface can contain constants and method declarations together; it is not restricted to one or the other. You can also declare one interface that inherits one or more other interfaces. For example, we could have the following two interfaces:
public interface LandMover { public void walk(); } public interface WaterMover { public void swim(); }
We may also want to combine these interfaces to make an interface that supports both walk and swim methods. We could define this interface as follows.
public interface AmphibiousMover extends LandMover, WaterMover { }
The interface AmphibiousMover inherits all of the members defined in the interfaces LandMover and WaterMover, which in this example are the methods walk and swim from their respective interfaces.
Note | Any of the overheads that are normally problematic when using multiple inheritance will be picked up by the compiler when inheriting multiple interfaces. For example, the interfaces LandMover and WaterMover could both contain a static final variable with the same name but different values. If you then implemented the AmphibiousMover interface and tried to access this constant variable, the compiler will pick up the error because it cannot possibly decipher which of the two variables you wish to use. This doesn't necessarily mean that these interfaces could not still be used. They would themselves still compile because you can cast your object to an interface type and then access the appropriate constant variable that way. The compiler will then know what interface to access the constant from. We will see about casting objects to interface types shortly. |
In order for a class to implement an interface, you need to use the keyword implements. For example, if we have a class, Alien, that needs to know the resolution of the displayable screen area, which is provided by constant variables in the GameData interface that we defined earlier, our Alien class could implement the GameData interface as follows.
public class Alien implements GameData { public void printResolution() { System.out.println(SCREEN_WIDTH + ", "+ SCREEN_HEIGHT); } }
The advantage in this case is that classes are not restricted to merely implementing one interface like they are restricted to only extending one other class. A class may still extend another class and implement multiple interfaces also. For example, we may declare the Alien class, which we could say extends the Creature class, like in previous examples, and implements the GameData and LandMover interfaces, as follows:
public class Alien extends Creature implements GameData, LandMover { public Alien(String greeting) { super(greeting); } public void printResolution() { System.out.println(SCREEN_WIDTH + ", "+ SCREEN_HEIGHT); } public void walk() { // must supply code body for walk method } public void speak() { System.out.println("Alien says: " + getGreeting()); } }
As you can see, we have declared and implemented the method walk in the Alien class. Because the Alien class has implemented the LandMover interface, it must implement this method complete with code body. This is our first glimpse at the real advantages of using interfaces; we have the ability to give our classes different labels and are assured that the class implements the methods associated with that label.
It is not possible to make an interface object. For starters, they do not provide an implementation for any methods that they declare, so to instantiate them would be completely unreasonable. You can, however, create an object of a class that implements an interface and then cast the object to a variable of the interface type. For example, take the Alien class that we have just defined. We can create an instance of the Alien class (the Alien class version that is implementing the LandMover interface previously defined), and then we can cast the Alien object to type LandMover.
public class MainClass { public static void main(String[] args) { Alien myAlien = new Alien("DakDakDak-Dak"); moveOnLand(myAlien); } public static void moveOnLand(LandMover landMover) { landMover.walk(); } }
As you can see, this gives us a great advantage; we are no longer just restricted to casting an object to a type that is within its acceptable class hierarchy where polymorphism could be used, but we are also now able to cast our objects to a type of an interface that its class implements, where we may also use polymorphism with the methods defined in the interface type to which we are casting. In the previous example, we passed the reference myAlien to the method moveOnLand, where it was received and cast to type LandMover. The method walk is then invoked from an object of type LandMover. This means we can use the method moveOnLand for any objects of classes that implement the LandMover interface. We are no longer restricted to objects that are subclasses of Creature. For example:
public class Submarine implements WaterMover { public void swim() { // handle swimming } }
If we had a method to handle a swimming object:
public void moveInWater(WaterMover waterMover) { waterMover.swim(); }
...we could pass a Submarine object to this method, as well as a Human object or any object that also implemented the interface WaterMover.
Going back to what was initially discussed, we can now give our objects multiple identities, casting them within their class hierarchy or to any interface type they implement. But remember, the object is never changed when casting; it will always be the same. Access to members of the object is what actually changes, as it takes a different identity, be it a class or interface implementation.
There may be a time when you want to know if an object implements an interface or not. It's quite likely that you may have a list of related types, such as Alien and Human, that are all Creature derivatives, and you want to run through the list invoking methods on the object defined by an interface. Only certain objects in the list implement that interface and therefore contain that method, and others do not. For an array of Creature types, which could be Alien or Human objects, the Alien class may implement the LandMover interface, whereas the Human class may implement both the LandMover and WaterMover interfaces. You want to run through this array only invoking the swim method on any objects that implement this method. We all know aliens cannot swim (well, I think they can't), but the list does not know which objects can swim and which cannot.
The simplest way to perform this task is to use the keyword instanceof to check if an object is an "instance of" a class or interface. For example, let's say we have the Alien class that only implements the LandMover interface and not the WaterMover interface. Then we have an object of either type Alien or Human cast to a reference of type Creature, and we no longer know whether the true type of the object is an Alien or a Human. Or more to the point, we do not know which of the interfaces our object implements to which we want to invoke the appropriate method.
Using the instanceof operator, we could perform this check in two different ways. First, we could check to see if the object implements the appropriate interfaces and then implement its respective method, as follows:
Creature myCreature = new Alien("DakDakDak-Dak"); if(myCreature instanceof LandMover) ((LandMover)myCreature).walk();
The implementation of invoking the walk method is quite logical when you think about it. First, the reference myCreature is of type Creature. The Creature class does not implement any walk method, so any such method cannot be invoked in terms of a Creature type object. We also cannot cast the object back down to its true type, as we do not know its true type at this stage. What we can do, however, is cast the object to type LandMover, as we know it is an object of this type, implementing the LandMover interface as we just checked. We can then invoke the walk method in terms of a LandMover cast object.
Alternatively, also using the instanceof operator, we can check if our object is of a class type Alien and then work from there.
Creature myCreature = new Alien("DakDakDak-Dak"); if(myCreature instanceof Alien) ((Alien)myCreature).walk();
Both methods have their advantages. If you have a list of many different unknown objects all cast as type Creature and many implement the LandMover interface, the interface checking would be better because it would be one check, whereas the class check would mean checking all different types of Creature subclass types. The class method would be better simply because of the control it gives back to you in knowing the true type of the object again, accessing other members specific to it, etc.
You should take note that using instanceof checks if an object is not only an instance of the true type class but also if the object is an instance of any super classes of its true type. For example, testing if(myAlien instanceof Creature) will return true also. A better example is that if(anyObject instanceof Object) will always return true, as all objects are ultimately derived from the Object class at the very top of the class hierarchy.
|
|