“Multiple inheritance” in Java Because an interface has no implementation at all—that is, there is no storage associated with an interface—there’s nothing to prevent many interfaces from being combined. This is valuable because there are times when you need to say, "An x is an a and a b and a c." In C++, this act of combining multiple class interfaces is called multiple inheritance, and it carries some rather sticky baggage because each class can have an implementation. In Java, you can perform the same act, but only one of the classes can have an implementation, so the C++ problems do not occur with Java when combining multiple interfaces: In a derived class, you aren’t forced to have a base class that is either abstract or "concrete" (one with no abstract methods). If you do inherit from a non-interface, you can inherit from only one. All the rest of the base elements must be interfaces. You place all the interface names after the implements keyword and separate them with commas. You can have as many interfaces as you want. You can upcast to each interface, because each interface is an independent type. The following example shows a concrete class combined with several interfaces to produce a new class: //: interfaces/Adventure.java // Multiple interfaces.
interface CanFight { void fight();
} interface CanSwim { void swim();
} interface CanFly { void fly();
} class ActionCharacter { public void fight() {} } class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly { public void swim() {} public void fly() {} } public class Adventure { public static void t(CanFight x) { x.fight(); } public static void u(CanSwim x) { x.swim(); } 230 Thinking in Java Bruce Eckel public static void v(CanFly x) { x.fly(); } public static void w(ActionCharacter x) { x.fight(); } public static void main(String[] args) { Hero h = new Hero();
t(h); // Treat it as a CanFight u(h); // Treat it as a CanSwim v(h); // Treat it as a CanFly w(h); // Treat it as an ActionCharacter } } ///:~ You can see that Hero combines the concrete class ActionCharacter with the interfaces CanFight, CanSwim, and CanFly. When you combine a concrete class with interfaces this way, the concrete class must come first, then the interfaces. (The compiler gives an error otherwise.) The signature for fight( ) is the same in the interface CanFight and the class ActionCharacter, and that fight( ) is not provided with a definition in Hero. You can extend an interface, but then you’ve got another interface. When you want to create an object, all the definitions must first exist. Even though Hero does not explicitly provide a definition for fight( ), the definition comes along with ActionCharacter; thus, it’s possible to create Hero objects.
In class Adventure, you can see that there are four methods that take arguments of the various interfaces and of the concrete class. When a Hero object is created, it can be passed to any of these methods, which means it is being upcast to each interface in turn. Because of the way interfaces are designed in Java, this works without any particular effort on the part of the programmer.
Keep in mind that one of the core reasons for interfaces is shown in the preceding example:
to upcast to more than one base type (and the flexibility that this provides). However, a second reason for using interfaces is the same as using an abstract base class: to prevent the client programmer from making an object of this class and to establish that it is only an interface.
This brings up a question: Should you use an interface or an abstract class? If it’s possible to create your base class without any method definitions or member variables, you should always prefer interfaces to abstract classes. In fact, if you know something is going to be a base class, you can consider making it an interface (this subject will be revisited in the chapter summary).
Exercise 12: (2) In Adventure.java, add an interface called CanClimb, following the form of the other interfaces.
Exercise 13: (2) Create an interface, and inherit two new interfaces from that interface.
Multiply inherit a third interface from the second two.2 Extending an interface with inheritance You can easily add new method declarations to an interface by using inheritance, and you can also combine several interfaces into a new interface with inheritance. In both cases you get a new interface, as seen in this example: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2 This shows how interfaces prevent the "diamond problem" that occurs with C++ multiple inheritance.
Interfaces 231!
//: interfaces/HorrorShow.java // Extending an interface with inheritance.
interface Monster { void menace();
} interface DangerousMonster extends Monster { void destroy();
} interface Lethal { void kill();
} class DragonZilla implements DangerousMonster { public void menace() {} public void destroy() {} } interface Vampire extends DangerousMonster, Lethal { void drinkBlood();
} class VeryBadVampire implements Vampire { public void menace() {} public void destroy() {} public void kill() {} public void drinkBlood() {} } public class HorrorShow { static void u(Monster b) { b.menace(); } static void v(DangerousMonster d) { d.menace();
d.destroy();
} static void w(Lethal l) { l.kill(); } public static void main(String[] args) { DangerousMonster barney = new DragonZilla();
u(barney);
v(barney);
Vampire vlad = new VeryBadVampire();
u(vlad);
v(vlad);
w(vlad);
} } ///:~ DangerousMonster is a simple extension to Monster that produces a new interface. This is implemented in DragonZilla. The syntax used in Vampire works only when inheriting interfaces. Normally, you can use extends with only a single class, but extends can refer to multiple base interfaces when building a new interface. As you can see, the interface names are simply separated with commas. Exercise 14: (2) Create three interfaces, each with two methods. Inherit a new interface that combines the three, adding a new method. Create a class by implementing the new interface and also inheriting from a concrete class. Now write four methods, each of which 32 Thinking in Java Bruce Eckel takes one of the four interfaces as an argument. In main( ), create an object of your class and pass it to each of the methods. Exercise 15: (2) Modify the previous exercise by creating an abstract class and inheriting that into the derived class. Name collisions when combining Interfaces You can encounter a small pitfall when implementing multiple interfaces. In the preceding example, both CanFight and ActionCharacter have identical void fight( ) methods. An identical method is not a problem, but what if the method differs by signature or return type?
Here’s an example: //: interfaces/InterfaceCollision.java package interfaces;
interface I1 { void f(); } interface I2 { int f(int i); } interface I3 { int f(); } class C { public int f() { return 1; } } class C2 implements I1, I2 { public void f() {} public int f(int i) { return 1; } // overloaded } class C3 extends C implements I2 { public int f(int i) { return 1; } // overloaded } class C4 extends C implements I3 { // Identical, no problem:
public int f() { return 1; } } // Methods differ only by return type:
//! class C5 extends C implements I1 {} //! interface I4 extends I1, I3 {} ///:~ The difficulty occurs because overriding, implementation, and overloading get unpleasantly mixed together. Also, overloaded methods cannot differ only by return type. When the last two lines are uncommented, the error messages say it all: InterfaceCollision.java:23: f() in C cannot implementf() in It; attempting to use incompatible return type found: int required: void InterfaceCollision.java:24: Interfaces I3 andh are incompatible; both define f(), but with different return type Using the same method names in different interfaces that are intended to be combined generally causes confusion in the readability of the code, as well. Strive to avoid it. nterfaces 233!
Adapting to an interface One of the most compelling reasons for interfaces is to allow multiple implementations for the same interface. In simple cases this is in the form of a method that accepts an interface, leaving it up to you to implement that interface and pass your object to the method. Thus, a common use for interfaces is the aforementioned Strategy design pattern. You write a method that performs certain operations, and that method takes an interface that you also specify. You’re basically saying, "You can use my method with any object you like, as long as your object conforms to my interface." This makes your method more flexible, general and reusable. For example, the constructor for the Java SE5 Scanner class (which you’ll learn more about in the Strings chapter) takes a Readable interface. You’ll find that Readable is not an argument for any other method in the Java standard library—it was created solely for Scanner, so that Scanner doesn’t have to constrain its argument to be a particular class.
This way, Scanner can be made to work with more types. If you create a new class and you want it to be usable with Scanner, you make it Readable, like this: //: interfaces/RandomWords.java // Implementing an interface to conform to a method.
import java.nio.;
import java.util.;
public class RandomWords implements Readable { private static Random rand = new Random(47);
private static final char[] capitals = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private static final char[] lowers = "abcdefghijklmnopqrstuvwxyz".toCharArray();
private static final char[] vowels = "aeiou".toCharArray();
private int count;
public RandomWords(int count) { this.count = count; } public int read(CharBuffer cb) { if(count-- == 0) return -1; // Indicates end of input cb.append(capitals[rand.nextInt(capitals.length)]);
for(int i = 0; i < 4; i++) { cb.append(vowels[rand.nextInt(vowels.length)]);
cb.append(lowers[rand.nextInt(lowers.length)]);
} cb.append(" ");
return 10; // Number of characters appended } public static void main(String[] args) { Scanner s = new Scanner(new RandomWords(10));
while(s.hasNext()) System.out.println(s.next());
} } /* Output:
Yazeruyac Fowenucor Goeazimom Raeuuacio Nuoadesiw Hageaikux Ruqicibui Numasetih Kuuuuozog 234 Thinking in Java Bruce Eckel Waqizeyoy ///:~ The Readable interface only requires the implementation of a read( ) method. Inside read( ), you add to the CharBuffer argument (there are several ways to do this; see the CharBuffer documentation), or return -l when you have no more input. Suppose you have a class that does not already implement Readable—how do you make it work with Scanner? Here’s an example of a class that produces random floating point numbers: //: interfaces/RandomDoubles.java import java.util.;
public class RandomDoubles { private static Random rand = new Random(47);
public double next() { return rand.nextDouble(); } public static void main(String[] args) { RandomDoubles rd = new RandomDoubles();
for(int i = 0; i < 7; i ++) System.out.print(rd.next() + " ");
} } /* Output:
0.7271157860730044 0.5309454508634242 0.16020656493302599 0.18847866977771732 0.5166020801268457 0.2678662084200585 0.2613610344283964 ///:~ Because you can add an interface onto any existing class in this way, it means that a method that takes an interface provides a way for any class to be adapted to work with that method.
This is the power of using interfaces instead of classes. Exercise 16: (3) Create a class that produces a sequence of chars. Adapt this class so that it can be an input to a Scanner object. Fields in interfaces Because any fields you put into an interface are automatically static and final, the interface is a convenient tool for creating groups of constant values. Before Java SE5, this was the only way to produce the same effect as an enum in C or C++. So you will see pre-Java SE5 code like this: //: interfaces/Months.java // Using interfaces to create groups of constants.
package interfaces;
public interface Months { int JANUARY = 1, FEBRUARY = 2, MARCH = 3, APRIL = 4, MAY = 5, JUNE = 6, JULY = 7, AUGUST = 8, SEPTEMBER = 9, OCTOBER = 10, NOVEMBER = 11, DECEMBER = 12;
} ///:~ Notice the Java style of using all uppercase letters (with underscores to separate multiple words in a single identifier) for static finals that have constant initializers. The fields in an interface are automatically public, so that is not explicitly specified. Interfaces 235!
With Java SE5, you now have the much more powerful and flexible enum keyword, so it rarely makes sense to use interfaces for constants anymore. However, you will probably run across the old idiom on many occasions when reading legacy code (the supplements for this book at www.MindView.net contain a complete description of the pre-Java SE5 approach to producing enumerated types using interfaces). You can find more details about using enums in the Enumerated Types chapter. Exercise 17: (2) Prove that the fields in an interface are implicitly static and final. Initializing fields in interfaces Fields defined in interfaces cannot be "blank finals," but they can be initialized with non-constant expressions. For example: //: interfaces/RandVals.java // Initializing interface fields with // non-constant initializers.
import java.util.;
public interface RandVals { Random RAND = new Random(47);
int RANDOM_INT = RAND.nextInt(10);
long RANDOM_LONG = RAND.nextLong() * 10;
float RANDOM_FLOAT = RAND.nextLong() * 10;
double RANDOM_DOUBLE = RAND.nextDouble() * 10;
} ///:~ Since the fields are static, they are initialized when the class is first loaded, which happens when any of the fields are accessed for the first time. Here’s a simple test: //: interfaces/TestRandVals.java import static net.mindview.util.Print.;
public class TestRandVals { public static void main(String[] args) { print(RandVals.RANDOM_INT);
print(RandVals.RANDOM_LONG);
print(RandVals.RANDOM_FLOAT);
print(RandVals.RANDOM_DOUBLE);
} } / Output:
8 -32032247016559954 -8.5939291E18 5.779976127815049 *///:~ The fields, of course, are not part of the interface. The values are stored in the static storage area for that interface. 36 Thinking in Java Bruce Eckel
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme