188 | | === Inheritance |
189 | | Classes can be extended so that you don't need to replicate lots of code. This is particularly useful when there is a hierarchical relationship between classes. For example, you can define a class Fruit and then extend it with sub-classes Orange, Apple, Banana. Each of the sub-classes meets the "isa" test: an Apple isa Fruit. When a class extends another class, it inherits all of the attributes and functions of the parent (super) class. So for example consider the following classes: |
190 | | {{{ |
191 | | class Fruit { |
192 | | String color; |
193 | | |
194 | | void eat(void) { |
195 | | ...munch munch... |
196 | | } |
197 | | } |
198 | | |
199 | | class Orange extends Fruit { |
200 | | int numSegments; |
201 | | } |
202 | | }}} |
203 | | |
204 | | You could create variables of these class types and use them in interesting ways: |
205 | | {{{ |
206 | | Fruit f; |
207 | | Orange o; |
208 | | Apple a; |
209 | | |
210 | | o = new Orange(); |
211 | | a = new Apple(); |
212 | | f = o; // legit because o "isa" Fruit |
213 | | f = a; // also legit because a "isa" Fruit |
214 | | f.eat(); // eat the apple |
215 | | |
216 | | o = a; // ERROR: a is not an Orange so can't be assigned to one |
217 | | }}} |
| 189 | === Class relationships |
| 190 | There two main ways classes can be related: |
| 191 | * Hierarchical: Classes that are related hierarchically will pass the "isa" test. For example, an Apple isa Fruit. A Car isa Vehicle. |
| 192 | * Containment : Classes that are related by containment will pass the "hasa" test. For example, an Apple hasa Seed. A Car hasa Passenger. |
| 193 | |
| 194 | ==== Class Hierarchy (Inheritance) |
| 195 | Inheritance provides a clean method for "factoring" your software. If many objects of the same type share some functionality, it's useful (important) to write and maintain that functionality in one place. With large code-bases, this makes code smaller, easier to write, easier to understand, and easier to repair (if you find a bug, you only need to fix it in one place). |
| 196 | |
| 197 | For example, you can define a class Fruit and then extend it with sub-classes such as Apple (and Orange, Banana, Kiwi, etc.). Each sub-class meets the "isa" test: an Apple isa Fruit. |
| 198 | |
| 199 | When a class extends another class, it inherits all of the attributes and functions of the parent (super) class. |
| 200 | |
| 201 | Create a new folder (InheritanceExample) and in it create the following java files: |
| 202 | |
| 203 | Fruit.java: |
| 204 | {{{ |
| 205 | public class Fruit { |
| 206 | int weight; // weight of the fruit (grams) |
| 207 | int sugar; // sugar contained (grams) |
| 208 | final int sweet = 10; // ratio to be "sweet" (percent) |
| 209 | |
| 210 | public Fruit(int weight_grams, int sugar_grams) { |
| 211 | this.weight = weight_grams; |
| 212 | this.sugar = sugar_grams; |
| 213 | } |
| 214 | |
| 215 | // Fruit is sweet if more than 10% sugar |
| 216 | public void eat() { |
| 217 | if (sugar * sweet > weight) { |
| 218 | System.out.println("Sweet!"); |
| 219 | } else { |
| 220 | System.out.println("Tart."); |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | }}} |
| 225 | |
| 226 | Apple.java: |
| 227 | {{{ |
| 228 | import java.lang.String; |
| 229 | |
| 230 | public class Apple extends Fruit { |
| 231 | String variant; |
| 232 | |
| 233 | public Apple(String variant, int weight_grams, int sugar_grams) { |
| 234 | super(weight_grams, sugar_grams); |
| 235 | this.variant = variant; |
| 236 | } |
| 237 | |
| 238 | @Override |
| 239 | public void eat() { |
| 240 | System.out.print("Eating a "+variant+" Apple: "); |
| 241 | super.eat(); |
| 242 | } |
| 243 | } |
| 244 | }}} |
| 245 | |
| 246 | Grapefruit.java: |
| 247 | {{{ |
| 248 | import java.lang.String; |
| 249 | |
| 250 | public class Grapefruit extends Fruit { |
| 251 | String color; |
| 252 | |
| 253 | public Grapefruit(String color, int weight_grams, int sugar_grams) { |
| 254 | super(weight_grams, sugar_grams); |
| 255 | this.color= color; |
| 256 | } |
| 257 | |
| 258 | @Override |
| 259 | public void eat() { |
| 260 | System.out.print("Eating a "+color+" Grapefruit: "); |
| 261 | super.eat(); |
| 262 | } |
| 263 | } |
| 264 | }}} |
| 265 | |
| 266 | You can then create a program that uses these classes named Inheritance.java |
| 267 | {{{ |
| 268 | public class Inheritance { |
| 269 | public static void main(String args[]) { |
| 270 | Apple a = new Apple("Honeycrisp", 100, 12); |
| 271 | Grapefruit g = new Grapefruit("Pink", 100, 4); |
| 272 | Fruit f = a; |
| 273 | f.eat(); |
| 274 | g.eat(); |
| 275 | // g=a; <<< illegal because an Apple is not a Grapefruit (fails isa test) |
| 276 | } |
| 277 | } |
| 278 | }}} |
| 279 | |
| 280 | Use the debugger to run and explore the Inheritance program and explore what happens in each line of the program. Be sure to step into functions to see how the code flows between the classes. Examine how the Apple class overrides and extends the eat() method of the Fruit class including: |
| 281 | * the use of the @Override notation to indicate to the reader that a superclass method is being overridden |
| 282 | * the use of the super.eat() call to invoke the eat method of the extended super class (Fruit) |