Skip to main content

Java Fundamental, Part 9 - Inheritance and polymorphism

Inheritance and polymorphism

Rancang program yang akan anda buat dengan bayangan kebutuhan ke depan bukan hanya kebutuhan saat anda bikin program saja.

If there were a way to write Java code such that you could take more vacations, how much would It be worth to you? What if you could write code that someone else could extend, easily? And if you could write code that was flexible, for those pesky last-minute spec changes, would that be something you're interested in? Then this is your lucky day. For just three easy payments of 60 minutes time, you can have all this. When you get on the Polymorphism Plan, you'll learn the 5 steps to better class design, the 3 tricks to polymorphism, the 8 ways to make flexible code, and if you act now-a bonus lesson on the 4 tips for exploiting inheritance. Don't delay, an offer this good will give you the design freedom and programming flexibility you deserve. It's quick. it's easy, and it's available now. Start today, and we'll throw in an extra level of abstraction!

Kita lihat contoh pada bab sebelumnya, yaitu mengenai bentuk (shape): triangle, circle, square dan amoeba. Kita lihat lagi bagaimana mekanisme inheritance bekerja.



You can read this as, “Square Inherits from Shape". "Circle Inherits from Shape", and soon. I removed rotate() and playSound() from the other shapes, so now there’s only one copy to maintain.

The shape class Is called the superclass of the other four classes. The other four are the subclasses of Shape. The subclasses Inherit the methods of the superclass. In other words. If the Shape class has the functionality, then the subclasses automatically get that same functionality.

Bagaimana dengan class Amoeba yang memiliki procedure rotate dan playSound yang berbeda sendiri? Bagaimana fungsionalitas yg berbeda itu dilakukan dengan inherits dari superclass-nya?


Seperti telah dibahas sebelumnya hal ini dilakukan dengan mekanisme overrides yaitu dengan override method dari class Shape. Pada saat dijalankan (run-time) JVM mengetahui method rotate() mana yang harus dijalankan (run) ketika seseorang meminta Amoeba untuk berputar.


  1. Bagaimana Inheritance Bekerja?

When you design with inheritance, you put common code in a class and then tell other more specific classes that the

common (more abstract) class is their superclass. When one class inherits from another, the subclass inherits from the superclass. 

In Java, we say that the subclass extends the superclass. An inheritance relationship means that the subclass inherits the members of the superclass. When we say "members of a class" we mean the instance variables and methods. For example, if PantherMan is a subclass of SuperHero, the PantherMan class automatically inherits the instance variables and methods common to all superheroes including suit, tights, specialPower, useSpecialPower() and so on. But the PantherMan subclass can add new methods and instance variables of its own, and it can override the methods it inherits from the superclass SuperHero.

FriedEggMan doesn't need any behaviour that's unique, so he doesn't override any methods. The methods and instance variables in SuperHero are sufficient. PanthenMan, though, has specific requirements for his suit

and special powers, so useSpecialPower() and putOnSuit() are both overridden in the PantherMan class.

Instance variables are not overridden because they don't need to be. They don't define any special behaviour, so a subclass can give an inherited instance variable any value it chooses. PantherMan can set his inherited tights to purple, while FriedEggMan sets his to white.








Rancangan runutan (tree) inheritance untuk program simulasi Animal

Imagine you're asked to design a simulation program that lets the user throw a bunch of different animals into an environment to see what happens. We don't have to code the thing now; we're mostly interested in the design. We've been given a list of some of the animals that will be in the program, but not all. We know that each animal will be represented by an object, and that the objects will move around in the environment, doing whatever it is that each particular type is programmed to do. 

And we want other programmers to be able to add new kinds of animals to the program at any time.

First, we have to figure out the common, abstract characteristics that all animals have, and build those characteristics into a class that all animal classes can extend.



Gunakan inheritance untuk menghindari duplikasi code dalam subclasses

We have five instance variables:

  1. picture - the file name representing the JPEG of this animal 

  2. food - the type of food this animal eats, Right now, there can be only two values: meal or grass. 

  3. hunger - an int representing the hunger level of the animal. It changes depending on when (and how much) the animal eats.

  4. boundaries - values representing the height and width of the 'space' (for example, 640 x 480) that the animals will roam around in.

  5. location - the X and Y coordinates for where the animal is in the space.

We have four methods:

  1. makeNoise() - behaviour for when the animal is supposed to make noise.

  2. eat() - behaviour for when the animal encounters its preferred food source, meat or grass.

  3. sleep() - behaviour for when the animal is considered asleep.

  4. roam() - behaviour for when the animal is not eating or sleeping (probably just wandering around waiting to bump into a food source or a boundary).

Bagaimana merancang inheritance?

Apakah semua Animal makan dengan cara yang sama?

Assume that we all agree on one thing: the instance variables will work for an Animal types. A lion will have his own value for picture, food (we're thinking meat), hunger, boundaries, and location. A hippo will have different values for his instance variables, but he'll still have the same variables that the other Animal types have. Same with dog, tiger, and so on. But what about behaviour?

Method yang mana yang harus kita override?

Does a lion make the same noise as a dog? Does a cat eat like a hippo? Maybe in your version, but in ours, eating and making noise are Animal-type specific. We can't figure out how to code those methods in such a way that they'd work for any animal. OK, that's not true. We could write the makeNoise() method, for example, so that all it does is playa sound file defined in an instance variable for that type, but that's not very specialised. Some animals might make different noises for different situations (like one for eating, and another when bumping into an enemy, etc.) So just as with the Amoeba overriding the Shape class rotate() method, to get more amoeba-specific (in other words, unique) behavior, we'll have to do the same for our Animal subclasses.

Kita cari lebih lanjut peluang untuk menerapkan inheritance

The class hierarchy is starting to shape up. We have each subclass override the makeNoise() and eat() methods, so that there's no mistaking a Dog bark from a Cat meow (quite insulting to both parties). And a Hippo won't eat like a Lion. But perhaps there's more we can do. We have to look at the subclasses of Animal, and see if two or more can be grouped together in some way, and given code that's common to only that new group. Wolf and Dog have similarities. So do Lion, Tiger, and Cat.



Finish the class hierarchy

Since animals already have an organisational hierarchy (the whole kingdom, genus, phylum thing), we can use the level that makes the most sense for class design. We'll use the biological "families" to organize the animals by making a Feline class and a Canine class. We decide that Canines could use a common room() method. because they tend to move In packs. We also see that Felines could use a common roam() method, because they tend to avoid others of their own kind. We’ll let Hippo continue to use Its Inherited roam() method the generic one It gets from Animal. So, we're done with the: design for now: come back to It later In the chapter.




Method mana yang akan dipanggil?

The Wolf class has four methods. One inherited from Animal, one inherited from Canine (which is actually an overridden version of a method in class Animal), and two overridden in the Wolf class. When you create a Wolf object and assign it to a variable, you can use the dot operator on that reference variable to invoke all four methods. But which version of those methods gets called?

When you call a method on an object reference, you're calling the most specific version of the method for that object type. In other words, 

the lowest one wins! 

"Lowest" meaning lowest on the inheritance tree. Canine is lower than Animal, and Wolf is lower than Canine, so invoking a method on a reference to a Wolf object means the JVM starts looking first in the Wolf class. If the JVM doesn't find a version of the method in the Wolf class, it starts walking back up the inheritance hierarchy until it finds a match.


    1. Using IS-A and HAS-A

Remember that when one class inherits from another, we say that the subclass extends the superclass. When you want to know if one thing should extend another, apply the IS-A test, 

Triangle IS-A Shape, yeah, that works. Cat IS-A Feline, that works too. Surgeon IS-A Doctor, still good.

Tub extends Bathroom, sounds reasonable. Until you apply the IS-A test.

To know if you've designed your types correctly, ask, "Does it make sense to say type X IS-A type Y?" If it doesn't, you know there's something wrong with the design, so if we apply the IS-A test, Tub IS-A Bathroom is definitely false.

What if we reverse it to Bathroom extends Tub? That still doesn't work, Bathroom IS-A Tub doesn't work.

Tub and Bathroom are related, but not through inheritance. Tub and Bathroom are joined by a HAS-A relationship. Does it make sense to say "Bathroom HAS-A Tub"? If yes, then it means that Bathroom has a Tub instance variable. In other words, Bathroom has a reference to a Tub, but Bathroom does not extend Tub and vice-versa.


Tapi tunggu dulu, ada lagi yang kurang!

The IS-A test works anywhere in the inheritance tree. If your inheritance tree is well-designed, the IS-A test should make sense when you ask any subclass if it IS-A any of its supertypes.

If class B extends class A, class B IS-A class A. This is true anywhere in the inheritance tree. If class C extends class B, class C passes the IS-A test for both Band A.

With an inheritance tree like the one shown here, you're always allowed to say "Wolf extends Animal" or "Wolf IS-A Animal". It makes no difference if Animal is the superclass of the superclass of Wolf. In fact, as long as Animal is somewhere in the inheritance hierarchy above Wolf, Wolf IS-A Animal will always be true. The structure of the Animal inheritance tree says to the world: "Wolf IS-A Canine, so Wolf can do anything a Canine can do. And Wolf IS-A Animal, so Wolf can do anything an Animal can do." It makes no difference if Wolf overrides some of the methods in Animal or Canine. As far as the world (of other code) is concerned, a Wolf can do those four methods. How he does them, or in which class they 1'e overridden makes no difference. A Wolf can makeNoise(), eat(), sleep (), and roam() because a Wolf extends from class Animal.



Inheritance lets you guarantee that all classes grouped under a certain supertype have all the methods that the supertype has.

In other words, you define a common protocol for a set of classes related through inheritance.

When you define methods in a superclass, that can be inherited by subclasses, you're announcing a kind of protocol to other code that says, "All my subtypes (i.e. subclasses) can do these things, with these methods that look like this…" In other words, you establish a contract.

    1. Polymorphism

Class Animal establishes a common protocol for all Animal subtypes:

And remember, when we say any Animal, we mean Animal and any class that extends from Animal. Which again means, any class that has Animal somewhere above it in the inheritance hierarchy,

But we're not even at the really cool part yet, because we saved the best--polymorphism--for last

When you define a supertype for a group of classes, any subclass of that supertype can be substituted where the supertype is expected.

Say, what?

Don 't worry. we're nowhere near done explaining it. Two pages from now, you'll be an expert.


"When we say "all the methods' we mean "all the Inheritable methods', which for now actually means, "all the public methods”, although later we'll refine that definition a bit more.



With polymorphism, the reference type can be a superclass of the actual object type.

When you declare a reference variable, any object that passes the IS-A test for the declared type of the reference variable can be assigned to that reference. In other words, anything that extends the declared reference variable type can be assigned to the reference variable. This lets you do thing like make polymorphism arrays.




Tapi tunggu dulu, ada lagi yang kurang!

You can have polymorphic arguments and return, types.

If you can declare a reference variable of a supertype, say, Animal, and assign a subclass object to it, say, Dog, think of how that might work when the reference is an argument to a method...

With polymorphism, you can write code that doesn't have to change when you introduce new subclass types into the program.

Remember that Vet class? If you write that Vet class using arguments declared as type Animal; your code can handle any Animal subclass. That means if others want to take advantage of your Vet class, all they have to do is make sure their new Animal types extend class Animal. The Vet methods will still work, even though the Vet class was written without any knowledge of the new Animal subtypes the Vet will be working on.


    1. Keeping the contract: rules for overriding

When you override a method from a superclass, you're agreeing to fulfil the contract. The contract that says. for example, ~I take no arguments and I return a boolean." In other words, the arguments and return types of your overriding method must look to the outside world exactly like the overridden method in the superclass. 

The methods are the contract.

If polymorphism is going to work. the Toaster's version of the overridden method from Appliance has to work at runtime. Remember. the compiler looks at the reference type to decide whether you can call a particular method on that reference. With an Appliance reference to a Toaster, the compiler cares only if class Appliance has the method you 're invoking on an Appliance reference. But at runtime, the jVM looks not at the reference type (Appliance) but at the actual Toaster object on the heap. So if the compiler has already approved the method call, the only way it can work is if the overriding method has the same arguments and return types. Otherwise. someone with an Appliance reference will call turnOn() as a no arg method, even though there's a version in Toaster that takes an int. Which one is called at runtime? The one in Appliance. In other words, the turnOn(int level) method in Toaster is not an override.'


  • Arguments must be the same, and return types must be compatible.

The contract of superclass defines how other code can use a method. Whatever the superclass takes as an argument. the subclass overriding the method must use that same argument. And whatever the superclass declares as a return type. the overriding method must declare either the same type. or a subclass type. Remember, a subclass object is guaranteed to be able to do anything its superclass declares. so it's safe to return a subclass where the superclass Is expected.

  • The method can't be less accessible.

That means the access level must be the same, or friendlier. That means you can't, for example, override a public method and make It private. What a shock that would be to the code invoking what It thinks (at compile time) is a public method. If suddenly at runtime the JVM slammed the door shut because the overriding version called at runtime Is private!

So far we've learned about two access levels: private and public. The other two are in the deployment chapter (Release your Code) and appendix B. There's also another rule about overriding related to exception handling, but we'll wait until the chapter on exceptions (Risky Behavior) to cover that.


    1. Overloading a method

Method overloading is nothing more than having two methods with the same name but different argument lists. Period. There's no polymorphism involved with overloaded methods! Overloading lets you make multiple versions of a method, with different argument lists, for convenience to the callers. For example, if you have a method that takes only an int, the calling code has to convert, say, a double into an int before calling your method. But if you overloaded the method with another version that takes a double, then you've made things easier for the caller. You'll see more of this when we look into constructors in the object lifecycle chapter. Since an overloading method isn't trying to fulfil the polymorphism contract defined by its superclass, overloaded methods have much more flexibility.


  • The return types can be different.

You're free to change the return types in overloaded methods, as long as the argument lists are different.

  • You can't change ONLY the return type.

If only the return type is different, it's not a valid overload-the compiler will assume you're trying to override the method. And even that won't be legal unless the return type is a subtype of the return type declared in the superclass. To overload a method, you MUST change the argument list, although you can change the return type to anything.

  • You can vary the access levels in any direction.

You're free to overload a method with a method that's more restrictive. It doesn't matter, since the new method isn't obligated to fulfil the contract of the overloaded method.









Comments