Presentation is loading. Please wait.

Presentation is loading. Please wait.

6: Reusing Classes Composition Inheritance Choosing composition vs. inheritance Upcasting The final keyword Initialization and class loading Exercises.

Similar presentations


Presentation on theme: "6: Reusing Classes Composition Inheritance Choosing composition vs. inheritance Upcasting The final keyword Initialization and class loading Exercises."— Presentation transcript:

1 6: Reusing Classes Composition Inheritance Choosing composition vs. inheritance Upcasting The final keyword Initialization and class loading Exercises

2 6: Reusing Classes One of the most compelling features about Java is code reuse. The trick is to use the classes without soiling the existing code. In this chapter you ’ ll see two ways to accomplish this. –The first is quite straightforward: You simply create objects of your existing class inside the new class. This is called composition –The second approach is more subtle. It creates a new class as a type of an existing class. You literally take the form of the existing class and add code to it without modifying the existing class. This magical act is called inheritance class B extends A  B is a kind of A –Dog extends Animal =>Dog is a kind of animal ExtendClass Bas e ExtendClass Base Class Extend Definition Scope UML

3 Composition syntax Until now, composition has been used quite frequently. You simply place object references inside new classes. class Car { Engine eng; Wheel w1,w2,w3,w4; …… } class WaterSource { private String s; WaterSource() { System.out.println("WaterSource()"); s = new String("Constructed"); } public String toString() { return s; } }

4 public class SprinklerSystem { private String valve1, valve2, valve3, valve4; WaterSource source; int i; float f; public String toString() { return "valve1 = " + valve1 + "\n" + "valve2 = " + valve2 + "\n" + "valve3 = " + valve3 + "\n" + "valve4 = " + valve4 + "\n" + "i = " + i + "\n" + "f = " + f + "\n" + "source = " + source; } public static void main(String[] args) { SprinklerSystem sprinklers = new SprinklerSystem(); System.out.println(sprinklers); } ///1.composition 2. toString 3.null valve1 = null valve2 = null valve3 = null valve4 = null i = 0 f = 0.0 source = null

5 Initialization Timing –At the point the objects are defined. This means that they’ll always be initialized before the constructor is called. –In the constructor for that class. –Right before you actually need to use the object. This is often called lazy initialization. It can reduce overhead in situations where object creation is expensive and the object doesn’t need to be created every time.

6 public class SprinklerSystem { private String valve1, valve2, valve3, valve4; WaterSource source; int i=10; float f=5.0; //specifying init public SprinklerSystem (){//constructor init valve1=new String( “ V1 ” ); valve2=new String( “ V2 ” ); valve3=new String( “ V3 ” ); valve4=new String( “ V4 ” ); } public String toString() { if(source==null) //lazy init source=new WaterSource(); return “……” ; } public static void main(String[] args) { SprinklerSystem sprinklers = new SprinklerSystem(); System.out.println(sprinklers); }

7 Inheritance syntax class B extends A  B is a kind of A –Dog extends Animal =>Dog is a kind of animal When you extend a class, you automatically get all the data members and methods in the base class. You ’ re always doing inheritance when you create a class, because unless you explicitly inherit from some other class, you implicitly inherit from Java ’ s standard root class Object

8 Inheritance syntax class Creature { String name; float age; Color color; void die() { …… }; } class Animal extends Creature{ Head head; Foot[] feet; void move(float x,float y) { …… } int getHungryState(){ …… }; } Do not overuse inheritance –Although inheritance gets a lot of emphasis while learning OOP, it doesn ’ t mean that you should use it everywhere you possibly can. On the contrary, you should use it sparingly, only when it ’ s clear that inheritance is useful.

9 Inheritance syntax class Cleanser { private String s = new String("Cleanser"); public void append(String a) { s += a; } public void dilute() { append(" dilute()"); } public void apply() { append(" apply()"); } public void scrub() { append(" scrub()"); } public void print() { System.out.println(s); } public static void main(String[] args) { Cleanser x = new Cleanser(); x.dilute(); x.apply(); x.scrub(); x.print(); }

10 Inheritance syntax public class Detergent extends Cleanser { // Add methods to the interface: public void foam() { append(" foam()"); } // Change a method: OVERRIDE!!! (access specifier) public void scrub() { append(" Detergent.scrub()"); super.scrub(); // Call base-class version } // Test the new class: public static void main(String[] args) { Detergent x = new Detergent(); x.dilute(); x.apply(); x.scrub(); x.foam(); x.print(); System.out.println("Testing base class:"); Cleanser.main(args); } } ///:~

11 Overloading methods with base-class class Homer { char doh(char c) { System.out.println("doh(char)"); return 'd'; } float doh(float f) { System.out.println("doh(float)"); return 1.0f; } class Milhouse {} class Bart extends Homer { void doh(Milhouse m) {} } public class Hide { public static void main(String[] args) { Bart b = new Bart(); b.doh(1); // doh(??) used b.doh('x'); b.doh(1.0f); b.doh(new Milhouse()); } } ///:~

12 Initializing the base class Inheritance doesn ’ t just copy the interface of the base class. When you create an object of the derived class, it contains within it a subobject of the base class. Java automatically inserts calls to the base-class constructor in the derived-class constructor.

13 Initializing the base class class Art { Art() { System.out.println("Art constructor"); } } class Drawing extends Art { Drawing() { System.out.println("Drawing constructor"); } } public class Cartoon extends Drawing { Cartoon() { System.out.println("Cartoon constructor"); } public static void main(String[] args) { Cartoon x = new Cartoon(); } } ///:~ Art constructor Drawing constructor Cartoon constructor

14 Initializing the base class Constructors with arguments –The above example has default constructors; that is, they don ’ t have any arguments. It ’ s easy for the compiler to call these because there ’ s no question about what arguments to pass. –If your base-class doesn ’ t have default constructor, or if you want to call a base-class constructor that has an argument, you must explicitly write the calls to the base-class constructor using the super keyword and the appropriate argument list: super(xxx) –Super must be in the first line of constructor body

15 Constructors with arguments class Game { Game(int i) { System.out.println("Game constructor"); } Game() { System.out.println("Game default constructor"); } } class BoardGame extends Game { BoardGame(int i) { super(i); System.out.println("BoardGame constructor"); } public class Chess extends BoardGame { Chess() { super(11); System.out.println("Chess constructor"); } public static void main(String[] args) { Chess x = new Chess(); } } ///:~ //if it ’ s omitted, what will happen?

16 Initialization with inheritance Loading class file->Static init->memory alloc & clear- >Specifying init->constructor In the process of loading a subclass, the loader notices that it has a base class, which will be loaded then. –This will happen whether or not you’re going to make an object of that base class. –Next, the static initialization in the root base class is performed, and then the next derived class, and so on. Order 1.Static init(Base)->Static init(Derived) 2.memory alloc & clear 3.Specifying init(Base)->constructor(Base) 4.Specifying init(Derived)->constructor(Derived)

17 Initialization with inheritance 1.class Insect { 2.int i = 9; 3.int j; 4.Insect() { prt("i = " + i + ", j = " + j); j = 39; } 5.static int x1 = prt("static Insect.x1 initialized"); 6.static int prt(String s) { System.out.println(s); return 47; } 7.} 8.public class Beetle extends Insect { 9.int k = prt("Beetle.k initialized"); 10.Beetle() { prt("k = " + k); prt("j = " + j); } 11.static int x2 = prt("static Beetle.x2 initialized"); 12.public static void main(String[] args) { 13.prt("Beetle constructor"); 14.Beetle b = new Beetle(); 15.} 16.} ///:~ static Insect.x1 initialized static Beetle.x2 initialized Beetle constructor i = 9, j = 0 Beetle.k initialized k = 47 j = 39

18 The final keyword Final data –final makes the value a constant –final makes the reference a constant. Once the reference is initialized to an object, it can never be changed to point to another object. However, the object itself can be modified Blank finals –fields that are declared as final but are not given an initialization value. –the blank final must be initialized (in constructor) before it is used Final arguments –This means that inside the method you cannot change what the argument reference points to (primitive: or its value) Final methods –method ’ s behavior is retained during inheritance and cannot be overridden. Final classes –State that you don ’ t want to inherit from this class or allow anyone else to do so.

19 class Value { int i = 1; } public class FinalData { // Can be compile-time constants final int i1 = 9; static final int VAL_TWO = 99; // Typical public constant: public static final int VAL_THREE = 39; // Cannot be compile-time constants: final int i4 = (int)(Math.random()*20); static final int i5 = (int)(Math.random()*20); Value v1 = new Value(); final Value v2 = new Value(); static final Value v3 = new Value(); // Arrays: final int[] a = { 1, 2, 3, 4, 5, 6 }; public void print(String id) { System.out.println( id + ": " + "i4 = " + i4 + ", i5 = " + i5); } public static void main(String[] args) { FinalData fd1 = new FinalData(); //! fd1.i1++; // Error: can't change value fd1.v2.i++; // Object isn't constant! fd1.v1 = new Value(); // OK -- not final for(int i = 0; i < fd1.a.length; i++) fd1.a[i]++; // Object isn't constant! //! fd1.v2 = new Value(); // Error: Can't //! fd1.v3 = new Value(); // change reference //! fd1.a = new int[3]; fd1.print("fd1"); System.out.println("Creating new FinalData"); FinalData fd2 = new FinalData(); fd1.print("fd1"); fd2.print("fd2"); } } ///:~

20 class Poppet { } public class BlankFinal { final int i = 0; // Initialized final final int j; // Blank final final Poppet p; // Blank final reference // Blank finals MUST be initialized in the constructor: BlankFinal() { j = 1; // Initialize blank final p = new Poppet(); } BlankFinal(int x) { j = x; // Initialize blank final p = new Poppet(); } public static void main(String[] args) { BlankFinal bf = new BlankFinal(); } } ///:~ Blank Final

21 class Gizmo { public void spin() {} } public class FinalArguments { void with(final Gizmo g) { //! g = new Gizmo(); // Illegal -- g is final } void without(Gizmo g) { g = new Gizmo(); // OK -- g not final g.spin(); } // void f(final int i) { i++; } // Can't change // You can only read from a final primitive: int g(final int i) { return i + 1; } public static void main(String[] args) { FinalArguments bf = new FinalArguments(); bf.without(null); bf.with(null); } } ///:~ Final Arguments

22 Any private methods in a class are implicitly final. Because you can’t access a private method, you can’t override it (even though the compiler doesn’t give an error message if you try to override it, you haven’t overridden the method, you’ve just created a new method). class WithFinals { public final void f() {System.out.println("WithFinals.f()");} // automatically "final": private void g() {System.out.println("WithFinals.g()"); } public void h() {System.out.println("WithFinals.h()"); } } class OverridingPrivate extends WithFinals { public void f() { System.out.println("OverridingPublic.f()"); } public void g() { // 或者 private void g() { System.out.println("OverridingPrivate.g()"); } void h() {System.out.println("Overriding.h()"); } } Final Method

23 class SmallBrain {} final class Dinosaur { int i = 7; int j = 1; SmallBrain x = new SmallBrain(); void f() {} } class Further extends Dinosaur {} // error: Cannot extend final class 'Dinosaur' public class Jurassic { public static void main(String[] args) { Dinosaur n = new Dinosaur(); n.f(); n.i = 40; n.j++; } } ///:~ Final Class

24 The best tack to take is to leave the data members private –you should always preserve your right to change the underlying implementation. In order to separate the interface from the implementation, you should set access specifiers as narrow as possible. –Client programmers can ’ t do anything but send messages to the public interface, then you can change anything that ’ s not public (e.g., “ friendly, ” protected, or private) without requiring modifications to client code. When overriding a method, we can ’ t narrow its access specifier (To ensure that all the interfaces of the base class are inherited. ) Access specifiers review

25 –available to anyone who inherits from this class –or anyone else in the same package. import java.util.*; class Villain { private int i; protected int read() { return i; } protected void set(int ii) { i = ii; } public Villain(int ii) { i = ii; } public int value(int m) { return m*i; } } //package test; public class Orc extends Villain { private int j; public Orc(int jj) { super(jj); j = jj; } public void change(int x) { set(x); } } ///:~ Protected members

26 Choosing composition vs. inheritance Composition is generally used when you want the features of an existing class inside your new class, but not its interface. –That is, you embed an object so that you can use it to implement functionality in your new class, but the user of your new class sees the interface you’ve defined for the new class rather than the interface from the embedded object. When you inherit, you take an existing class and make a special version of it. –In general, this means that you’re taking a general-purpose class and specializing it for a particular need. –The is-a relationship is expressed with inheritance, and the has-a relationship is expressed with composition –For example, you’ll see that it would make no sense to compose a car using a vehicle object—a car doesn’t contain a vehicle, it is a vehicle.

27 Guaranteeing proper cleanup Java doesn ’ t have the C++ concept of a destructor, a method that is automatically called when an object is destroyed. So if you want something cleaned up for a class, you must explicitly write a special method to do it, and make sure that the client programmer knows that they must call this method. You should follow the same order that is imposed by a C++ compiler on its destructors: First perform all of the cleanup work specific to your class, in the reverse order of creation.

28 Guaranteeing proper cleanup import java.util.*; class Shape { Shape(int i) { System.out.println("Shape constructor"); } void cleanup() { System.out.println("Shape cleanup"); } } class Circle extends Shape { Circle(int i) { super(i); System.out.println("Drawing a Circle"); } void cleanup() { System.out.println("Erasing a Circle"); super.cleanup(); }

29 Upcasting The most important aspect of inheritance is not that it provides methods for the new class. It ’ s the relationship expressed between the new class and the base class. –This relationship can be summarized by saying “ The new class is a type of the existing class. ” –This description is not just a fanciful way of explaining inheritance — it ’ s supported directly by the language. One of the clearest ways to determine whether you should use composition or inheritance is to ask whether you’ll ever need to upcast from your new class to the base class. –If you must upcast, then inheritance is necessary, –but if you don’t need to upcast, then you should look closely at whether you need inheritance.

30 Upcasting // Inheritance & upcasting. import java.util.*; class Instrument { public void play() {} static void tune(Instrument i) { //... i.play(); } // Wind objects are instruments // because they have the same interface: public class Wind extends Instrument { public static void main(String[] args) { Wind flute = new Wind(); Instrument.tune(flute); // Upcasting //Instrument ii = flute; // Instrument.tune(ii); } } ///:~

31 Exercises 9. Create a base class with only a nondefault constructor, and a derived class with both a default (no-arg) and nondefault constructor. In the derived-class constructors, call the base-class constructor. ============ 18. Create a class with a static final field and a final field and demonstrate the difference between the two.


Download ppt "6: Reusing Classes Composition Inheritance Choosing composition vs. inheritance Upcasting The final keyword Initialization and class loading Exercises."

Similar presentations


Ads by Google