Thinking in Java 3rd Edition phần 4 pptx

119 527 0
Thinking in Java 3rd Edition phần 4 pptx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Chapter 7: Polymorphism 327 Shape draw() erase() Circle draw() erase() Square draw() erase() Triangle draw() erase() This can be called a pure “is-a” relationship because the interface of a class establishes what it is. Inheritance guarantees that any derived class will have the interface of the base class and nothing less. If you follow the above diagram, derived classes will also have no more than the base class interface. Feedback This can be thought of as pure substitution, because derived class objects can be perfectly substituted for the base class, and you never need to know any extra information about the subclasses when you’re using them: Circle, Square, Line, or new type of Shape Talks to Shape Message "Is-a" relationship That is, the base class can receive any message you can send to the derived class because the two have exactly the same interface. All you need to do is upcast from the derived class and never look back to see what exact type of object you’re dealing with. Everything is handled through polymorphism. Feedback When you see it this way, it seems like a pure “is-a” relationship is the only sensible way to do things, and any other design indicates muddled thinking and is by definition broken. This too is a trap. As soon as you start thinking this way, you’ll turn around and discover that extending the interface (which, unfortunately, the keyword extends seems to encourage) is the perfect solution to a particular problem. This could be 328 Thinking in Java www.BruceEckel.com termed an “is-like-a” relationship because the derived class is like the base class—it has the same fundamental interface—but it has other features that require additional methods to implement: Useful void f() void g() void f() void g() void u() void v() void w() MoreUseful } Assume this represents a big interface "Is-like-a" } Extending the interface While this is also a useful and sensible approach (depending on the situation) it has a drawback. The extended part of the interface in the derived class is not available from the base class, so once you upcast you can’t call the new methods: Useful part Talks to Useful object Message MoreUseful part If you’re not upcasting in this case, it won’t bother you, but often you’ll get into a situation in which you need to rediscover the exact type of the object so you can access the extended methods of that type. The following section shows how this is done. Feedback Chapter 7: Polymorphism 329 Downcasting and run time type identification Since you lose the specific type information via an upcast (moving up the inheritance hierarchy), it makes sense that to retrieve the type information—that is, to move back down the inheritance hierarchy—you use a downcast. However, you know an upcast is always safe; the base class cannot have a bigger interface than the derived class, therefore every message you send through the base class interface is guaranteed to be accepted. But with a downcast, you don’t really know that a shape (for example) is actually a circle. It could instead be a triangle or square or some other type. Feedback Useful void f() void g() void f() void g() void u() void v() void w() MoreUseful } Assume this represents a big interface "Is-like-a" } Extending the interface To solve this problem there must be some way to guarantee that a downcast is correct, so you won’t accidentally cast to the wrong type and then send a message that the object can’t accept. This would be quite unsafe. Feedback In some languages (like C++) you must perform a special operation in order to get a type-safe downcast, but in Java every cast is checked! So even though it looks like you’re just performing an ordinary parenthesized cast, at run time this cast is checked to ensure that it is in fact the type you think it is. If it isn’t, you get a ClassCastException. This act of checking 330 Thinking in Java www.BruceEckel.com types at run time is called run-time type identification (RTTI). The following example demonstrates the behavior of RTTI: //: c07:RTTI.java // Downcasting & Run-Time Type Identification (RTTI). // {ThrowsException} class Useful { public void f() {} public void g() {} } class MoreUseful extends Useful { public void f() {} public void g() {} public void u() {} public void v() {} public void w() {} } public class RTTI { public static void main(String[] args) { Useful[] x = { new Useful(), new MoreUseful() }; x[0].f(); x[1].g(); // Compile time: method not found in Useful: //! x[1].u(); ((MoreUseful)x[1]).u(); // Downcast/RTTI ((MoreUseful)x[0]).u(); // Exception thrown } } ///:~ As in the diagram, MoreUseful extends the interface of Useful. But since it’s inherited, it can also be upcast to a Useful. You can see this happening in the initialization of the array x in main( ). Since both objects in the array are of class Useful, you can send the f( ) and g( ) methods to both, and if you try to call u( ) (which exists only in MoreUseful) you’ll get a compile-time error message. Feedback If you want to access the extended interface of a MoreUseful object, you can try to downcast. If it’s the correct type, it will be successful. Otherwise, Chapter 7: Polymorphism 331 you’ll get a ClassCastException. You don’t need to write any special code for this exception, since it indicates a programmer error that could happen anywhere in a program. Feedback There’s more to RTTI than a simple cast. For example, there’s a way to see what type you’re dealing with before you try to downcast it. All of Chapter 10 is devoted to the study of different aspects of Java run-time type identification. Feedback Summary Polymorphism means “different forms.” In object-oriented programming, you have the same face (the common interface in the base class) and different forms using that face: the different versions of the dynamically bound methods. Feedback You’ve seen in this chapter that it’s impossible to understand, or even create, an example of polymorphism without using data abstraction and inheritance. Polymorphism is a feature that cannot be viewed in isolation (like a switch statement can, for example), but instead works only in concert, as part of a “big picture” of class relationships. People are often confused by other, non-object-oriented features of Java, like method overloading, which are sometimes presented as object-oriented. Don’t be fooled: If it isn’t late binding, it isn’t polymorphism. Feedback To use polymorphism—and thus object-oriented techniques—effectively in your programs you must expand your view of programming to include not just members and messages of an individual class, but also the commonality among classes and their relationships with each other. Although this requires significant effort, it’s a worthy struggle, because the results are faster program development, better code organization, extensible programs, and easier code maintenance. Feedback Exercises Solutions to selected exercises can be found in the electronic document The Thinking in Java Annotated Solution Guide, available for a small fee from www.BruceEckel.com. 332 Thinking in Java www.BruceEckel.com 1. Add a new method in the base class of Shapes.java that prints a message, but don’t override it in the derived classes. Explain what happens. Now override it in one of the derived classes but not the others, and see what happens. Finally, override it in all the derived classes. Feedback 2. Add a new type of Shape to Shapes.java and verify in main( ) that polymorphism works for your new type as it does in the old types. Feedback 3. Change Music3.java so that what( ) becomes the root Object method toString( ). Try printing the Instrument objects using System.out.println( ) (without any casting). Feedback 4. Add a new type of Instrument to Music3.java and verify that polymorphism works for your new type. Feedback 5. Modify Music3.java so that it randomly creates Instrument objects the way Shapes.java does. Feedback 6. Create an inheritance hierarchy of Rodent: Mouse, Gerbil, Hamster, etc. In the base class, provide methods that are common to all Rodents, and override these in the derived classes to perform different behaviors depending on the specific type of Rodent. Create an array of Rodent, fill it with different specific types of Rodents, and call your base-class methods to see what happens. Feedback 7. Modify Exercise 6 so that Rodent is an abstract class. Make the methods of Rodent abstract whenever possible. Feedback 8. Create a class as abstract without including any abstract methods, and verify that you cannot create any instances of that class. Feedback 9. Add class Pickle to Sandwich.java. Feedback 10. Modify Exercise 6 so that it demonstrates the order of initialization of the base classes and derived classes. Now add member objects to both the base and derived classes, and show the Chapter 7: Polymorphism 333 order in which their initialization occurs during construction. Feedback 11. Create a base class with two methods. In the first method, call the second method. Inherit a class and override the second method. Create an object of the derived class, upcast it to the base type, and call the first method. Explain what happens. Feedback 12. Create a base class with an abstract print( ) method that is overridden in a derived class. The overridden version of the method prints the value of an int variable defined in the derived class. At the point of definition of this variable, give it a nonzero value. In the base-class constructor, call this method. In main( ), create an object of the derived type, and then call its print( ) method. Explain the results. Feedback 13. Following the example in Transmogrify.java, create a Starship class containing an AlertStatus reference that can indicate three different states. Include methods to change the states. Feedback 14. Create an abstract class with no methods. Derive a class and add a method. Create a static method that takes a reference to the base class, downcasts it to the derived class, and calls the method. In main( ), demonstrate that it works. Now put the abstract declaration for the method in the base class, thus eliminating the need for the downcast. Feedback 335 8: Interfaces & Inner Classes Interfaces and inner classes provide more sophisticated ways to organize and control the objects in your system. C++, for example, does not contain such mechanisms, although the clever programmer may simulate them. The fact that they exist in Java indicates that they were considered important enough to provide direct support through language keywords. Feedback In Chapter 7, you learned about the abstract keyword, which allows you to create one or more methods in a class that have no definitions—you provide part of the interface without providing a corresponding implementation, which is created by inheritors. The interface keyword produces a completely abstract class, one that provides no implementation at all. You’ll learn that the interface is more than just an abstract class taken to the extreme, since it allows you to perform a variation on C++’s “multiple inheritance,” by creating a class that can be upcast to more than one base type. Feedback At first, inner classes look like a simple code-hiding mechanism: you place classes inside other classes. You’ll learn, however, that the inner class does more than that—it knows about and can communicate with the surrounding class—and that the kind of code you can write with inner classes is more elegant and clear, although it is a new concept to most. It takes some time to become comfortable with design using inner classes. Feedback Interfaces The interface keyword takes the abstract concept one step further. You could think of it as a “pure” abstract class. It allows the creator to establish the form for a class: method names, argument lists, and return 336 Thinking in Java www.BruceEckel.com types, but no method bodies. An interface can also contain fields, but these are implicitly static and final. An interface provides only a form, but no implementation. Feedback An interface says: “This is what all classes that implement this particular interface will look like.” Thus, any code that uses a particular interface knows what methods might be called for that interface, and that’s all. So the interface is used to establish a “protocol” between classes. (Some object-oriented programming languages have a keyword called protocol to do the same thing.) Feedback To create an interface, use the interface keyword instead of the class keyword. Like a class, you can add the public keyword before the interface keyword (but only if that interface is defined in a file of the same name) or leave it off to give package access, so that it is only usable within the same package. Feedback To make a class that conforms to a particular interface (or group of interfaces) use the implements keyword. implements says “The interface is what it looks like, but now I’m going to say how it works.” Other than that, it looks like inheritance. The diagram for the instrument example shows this: [...]... placing the class definition inside a surrounding class: Feedback //: c08:Parcel1 .java // Creating inner classes public class Parcel1 { class Contents { private int i = 11; public int value() { return i; } } 352 Thinking in Java www.BruceEckel.com class Destination { private String label; Destination(String whereTo) { label = whereTo; } String readLabel() { return label; } } // Using inner classes looks... confusion in the readability of the code, as well Strive to avoid it Feedback Chapter 8: Interfaces & Inner Classes 343 Extending an interface with inheritance You can easily add new method declarations to an interface 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: //: c08:HorrorShow .java. .. last two lines are uncommented, the error messages say it all: InterfaceCollision .java: 23: f( ) in C cannot implement f( ) in I1; attempting to use incompatible return type found : int required: void InterfaceCollision .java: 24: interfaces I3 and I1 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... 342 Thinking in Java www.BruceEckel.com identical void fight( ) method This is not a problem, because the method is identical in both cases, but what if it isn’t? Here’s an example: //: c08:InterfaceCollision .java interface interface interface class C { I1 { void f(); } I2 { int f(int i); } I3 { int f(); } public int f() { return 1; } } class C2 implements I1, I2 { public void f() {} public int f(int... Nesting a class within a scope public class Parcel5 { private void internalTracking(boolean b) { if(b) { class TrackingSlip { private String id; TrackingSlip(String s) { id = s; } String getSlip() { return id; } } 358 Thinking in Java www.BruceEckel.com TrackingSlip ts = new TrackingSlip("slip"); String s = ts.getSlip(); } // Can't use it here! Out of scope: //! TrackingSlip ts = new TrackingSlip("x"); }... getBase(int i) { return new Base(i) { { System.out.println("Inside instance initializer"); } public void f() { System.out.println( "In anonymous f()"); } }; } public static void main(String[] args) { Base base = getBase (47 ); base.f(); monitor.expect(new String[] { "Base constructor, i = 47 ", "Inside instance initializer", "In anonymous f()" }); } } ///:~ In this case, the variable i did not have to be final... scope of a method (instead of the scope of another class) This is called a local inner class: //: c08:Parcel4 .java // Nesting a class within a method Chapter 8: Interfaces & Inner Classes 357 public class Parcel4 { public Destination dest(String s) { class PDestination implements Destination { private String label; private PDestination(String whereTo) { label = whereTo; } public String readLabel() {... Parcel5 .java public class Parcel8 { // Argument must be final to use inside // anonymous inner class: public Destination dest(final String dest) { return new Destination() { private String label = dest; public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel8 p = new Parcel8(); Destination d = p.dest("Tanzania"); } } ///:~ If you’re defining an anonymous inner... it is never directly used inside the anonymous class Feedback Here’s the “parcel” theme with instance initialization Note that the arguments to dest( ) must be final since they are used within the anonymous class: //: c08:Parcel9 .java // Using "instance initialization" to perform // construction on an anonymous inner class import com.bruceeckel.simpletest.*; 362 Thinking in Java www.BruceEckel.com ... problems seen in C++ do not occur with Java when combining multiple interfaces: Abstract or Concrete Base Class interface 1 interface 2 interface n Base Class Methods interface 1 interface 2 interface n In a derived class, you aren’t forced to have a base class that is either an abstract or “concrete” (one with no abstract methods) If you do inherit from a non-interface, you can inherit from only . be combined generally causes confusion in the readability of the code, as well. Strive to avoid it. Feedback 344 Thinking in Java www.BruceEckel.com Extending an interface with inheritance. separate multiple words in a single identifier) for static finals that have constant initializers. Feedback 346 Thinking in Java www.BruceEckel.com The fields in an interface are automatically. declarations to an interface 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:

Ngày đăng: 14/08/2014, 00:21

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan