590 likes | 674 Views
FIT1002 Computer Programming Inheritance. Objectives. By the end of this lecture, students should: understand classes as types of objects understand the different uses of inheritance understand the use of concrete and abstract classes
E N D
Objectives • By the end of this lecture, students should: • understand classes as types of objects • understand the different uses of inheritance • understand the use of concrete and abstract classes • understand the difference between single and multiple inheritance • understand method polymorphism and its relation to late binding • understand “static” variables and methods • understand how inheritance is used to define the basic Java API • be able to extract classes from a problem specification • define an effective class structure for simple specifications • be able to extend classes supplied as a black box • Reading: Savitch, Chapter 7 (Inheritance) • Savitch, p 251-267 (Static Variables and Methods) • Savitch Section 8.1 (Polymorphism, late binding)
Poisonous Edible Fruit Basic OO: Revision • Every object has a particular type: a class • Classes are organized with “is-a” relationships, for example “An Edible Fruit is a Fruit”. • “is-s” relationships generate a super-class -> sub-class relationship • A sub-class is a “specialization” of the super-class. • The super-class is a generalization of the sub-class.
Poisonous Edible Fruit Basic Inheritance • Sub-classes inherit all properties from the super-class • instance variables • methods • For example, both edible and poisonous fruit have • a color • a name color: String name: String
Poisonous Edible Fruit Basic Inheritance • Sub-classes can add additional properties to the inherited ones • instance variables • methods • For example, both Edible fruit have a taste and we are interested in • their price, while Poisonous fruit cause some particular symptom color: String name: String taste: String price: int symptom: String
Poisonous Edible Orange Fruit Apple Basic Inheritance • Inheritance Hierarchies are not restricted to a single level. • They can be arbitrarily deep (and are transitive) • They cannot contain cycles. color: String name: String taste: String price: int symptom: String
Fruit Basic Inheritance public class Fruit { private String color; private String name; public String getName() { return name; } public void setName(String aName) { this.name = aName; } public String getColor() { return color; } public void setColor(String aColor) { this.color = aColor; } } color: String name: String
Edible Fruit Basic Inheritance public class Edible extends Fruit { private String taste; private int price; public String getTaste() { return taste; } public void setTaste(String aTaste) { this.taste=aTaste; } public int getPrince() { return price; } public void setPrice(int aPrice) { this.price=aPrice; } } color: String name: String taste: String price: int
Poisonous Fruit Basic Inheritance public class Poisonous extends Fruit { private String symptom; public String getSymptom() { return symptom; } public void setSymptom( String aSymptom) { this.symptom=aSymptom; } } color: String name: String symptom: String
Edible Orange Fruit Apple Basic Inheritance public class Apple extends Edible { } public class Orange extends Edible { } color: String name: String taste: String price: int
Inheritance and Private • Recall: “private” variables are not directly accessible as an instance variable in a sub-class. • If we want to inherit them fully, so that this is possible, we need to declare them public or drop the access modifier completely: public class Edible extends Fruit { String taste; int price; … }
Inheritance and Private • A better way is to define proper set/get methods (as before). • We can then access these instance variables with the get/set methods in the subclasses, even though we can not access them directly as an instance variable: public class Fruit { private String color; private String name; public String getName() { return name; } public void setName(String aName) { this.name = aName; } …
Inheritance and Private • Example: The following access will now work: Apple a = new Apple(); a.setTaste(“Yummy”); System.out.println(a.getTaste());
Abstract Classes (advanced, optional) • Not all classes are meant to have instances. • Some classes are only used for a clean conceptual program structure. • Example: if we model a Green Grocer, we will not ever have a “generic fruit” on the shelves, they will always be concrete type of fruit: Thus the classes “Fruit” and “Edible” are not meant to have instances. We will only instantiate “Apple” and “Orange”.(Hopefully we won’t need “Poisonous” at all). • You can declare a class as “abstract” to declare that it cannot be instantiated directly. Nothing else in the class definition changes. • Note: an abstract class without subclasses does not make sense. public abstract class Fruit {… }
Overriding an Initialization Value • If an inherited instance variable needs to have different initialization values in different subclasses, it must be initialized in a constructor. • Note: to be on the safe side, all initializations should be performed in constructors. public class Apple extends Edible { public Apple(int aPrice) { setColor("red"); setName("Red Delicious"); setTaste("sweet"); setPrice(aPrice); } } Note how the private instance variables defined in the super-classes are accessed via setMethods.
Overriding an Initialization Value • If an inherited instance variable needs to have different initialization values in different subclasses, it must be initializaed in a constructor. • Note: to be on the safe side, all initializations should be performed in constructors. public class Orange extends Edible { public Orange(int aPrice) { setColor(“orange”); setName(“Naval”); setTaste(”juicy"); setPrice(aPrice); } } Note how the private instance variables defined in the super-classes are accessed via setMethods.
Overriding a Method • Methods are inherited in the same way from superclasses. • If a methods needs to behave differently in a subclass, it can simply be overridden by redefining it in the subclass. • Example: Account balance: float deposit(float): void withdraw(float): boolean getBalance(): float Savings Credit Cheque withdrawFee: float creditLimit : float …..
Overriding a Method public class Account { float balance = 0.0f; … public boolean withdraw(float amount) { if ( balance - amount >= 0.0f ) { balance -= amount; return true; } else return false; } …
Overriding a Method public class Savings extends Account { private float bankFee; … public boolean withdraw (float amount) { if (balance - amount - bankFee >= 0.0f) { balance = balance - amount - bankFee; return true; } return false; } …
Method Signature and Overriding • To override a method it must have exactly the same signature: • same name • same parameters (and parameter types) • it must also have the • same return type • The only exception to this rule is that the return-type of an overriding method in a subclass may be more specific the return type given in the superclass.
Method Signature and Overriding Example: A general Bank Card can be linked to any account, but a Credit Card only to a Credit Account. Bank Card linkedAccount(): Account Credit Card linkedAccount(): Credit
Calling a Super Class Method • You can invoke methods in the super-class from a sub-class object. • The syntax for this is “super.methodname(…parameters…)” • This is often useful to “extend” the functionality of the super-class method. • Example: withdrawing an amount x from a Savings Account is the same as withdrawing (x+bank fees) from a generic account:
Calling a Super Class Method • public class Savings extends Account • { • private float bankFee; • … • public boolean withdraw (float amount) • { • return super.withdraw(amount+bankFee); • } • …
Casting Objects (Advanced, optional) • A class is like a type for object, so you would expect to be able to • use type casts. This is possible in two directions • “upcasting”: changing the type to a superclassSavings s = new Savings();Account a = (Account) s;“upcasting” is safe. • “Downcasting”: changing the type to a subclassSavings s = new Savings();Account a = (Account) s;Savings s1 = (Savings) a;“downcasting” is dangerous: unless the object was originally created as an object of the target class it may not have all instance variables!
Polymorphism and Late Binding The final question for method calls when using inheritance is, which method implementation is actually called? Consider the following scenario.
Polymorphism and Late Binding public class Color { public String test() { return "I'm a generic color"; } } public class blue extends Color { public String test() { return "I'm blue"; } } public class red extends Color { public String test() { return "I'm red"; } }
Polymorphism and Late Binding Which method implementation is actually called?? Consider: test(0) vs test(1) public class Test { public String test(int which) { Color c=null; if (which==0) c = new blue(); else c = new red(); return c.test(); } }
Polymorphism and Late Binding c is a Color object, but (depending on input is actually instantiated as a Blue or a Red object). Each of these classes implements the test method. public class Test { public String test(int which) { Color c=null; if (which==0) c = new blue(); else c = new red(); return c.test(); } }
Polymorphism and Late Binding The code below will print “I’m blue” for input 0 and “I’m red” for any other input. The decision which method implementation is used is based made on the type of object to which the message is sent. public class Test { public String test(int which) { Color c=null; if (which==0) c = new blue(); else c = new red(); return c.test(); } }
Late Binding vs Early Binding Note that there is no way how Java could have decided at compile time which method implementation to use. This has to be decided at run time. This mechanism is called “late binding” and is used to implement method polymorphism (method overriding). Note that in contrast to this the decision between overloaded methods and operators (methods with the same name but different parameter signatures) can always be decided at compile time. This is called “early binding”.
Early Binding Demonstration Note how call of “decide()” is processed via early binding because it is overloaded. The method “test” will always return “I’m a generic color” independent of the input value. public class OtherTest { public String test(int which) { Color c=null; if (which==0) c = new blue(); else c = new red(); return decide(c); } public String decide(Color c) { return "I'm a generic color"; } public String decide(blue c) { return "I'm blue"; } public String decide(red c) { return "I'm red"; } }
The “Überclass” Object • There is a special class “Object” • Every class that you define automatically is a subclass of Object. • you don’t need to define “extends Object”. • Object contains a toString() method • it is inherited for every class that you defined • you can thus immediately print a representation of any objectSystem.out.println(x.toString());for debugging purposes • Check in the Java API what this prints! Usually you will want to override this method with a more useful version.
Class Properties • Some properties of objects are really not associated with the instances of the class but with the class itself. • Example: the financial institutions duty (FID) is a government fee on all transactions of Cheque accounts. It is a percentage fee set by the government an identical for all Cheque accounts. It thus belongs to the class and not to the instances.
Static Variables • Class properties in Java are declared with the • keyword “static”. • The value of a class property is stored with the class. public class Cheque extends Account { private float bankFee=1.2f; private static float FID=0.1f; //percentage public boolean withdraw (float amount) { float amountFID=amount*FID/100; return super.withdraw(amount+bankFee); } }
Using Static Variables • The name of a static variable can be used as if it would be a normal instance variable. • Cheque c = new Cheque(); • System.out.println(c.FID); • It can also be accessed via the class name, even if there is no instance at all • System.out.println(Cheque.FID);
Using Static Variables Example: Keeping a counter of the number of Objects in a particular class. public class Apple extends Edible { public static int count = 0; public Apple(int aPrice) { setColor("red"); setName("Red Delicious"); setTaste("sweet"); setPrice(aPrice); count ++; } }
Referencing Static Variables via the Class Apple a = new Apple(11.2); Apple b = new Apple(11.2); Apple c = new Apple(11.2); int numApples = Apple.count; System.out.println(“You have generated “+numApples+” Apples”);
Inheritance of Static Variables • Static Variables are inherited in the same way as normal instance variables. • Keep in mind that you cannot factor out in the same way. • Example: you want to count Apples as well as Oranges, • you cannot move “count” into the “Fruit” class. • you need separate counts in “Apple” and “Orange”
Static Methods • Methods can also be defined as “static” • Like for variables this means that they belong to the class • This is generally used for methods that do not have to be executed in the context of a particular object. • Example: You want to write a method that totals the balances in an array of accounts. This method has to be declared in some class, but it it not associated with a particular object. Solution: define it static in Account. • Static Methods can be invoked either using an object in the class or the class itself.
Static Methods public class Account { float balance = 0.0f; public static float totalAccounts(Account[] as) { float sum = 0f; for (Account a : as) sum += a.balance; return sum; } … } A static methd can be invoked using the class name or an instance: Account[] as = …; System.out.println(Account.totalAccounts(as)); Account a = new Account(); System.out.println(a.totalAccounts(as)); in the latter case it does not matter which instance it is invoked for.
“Service Classes” - Libraries • Static Methods are often used to generate libraries of functions. • The java class Math is such a library • This explains calls like • Math.sqrt(12.4); these are simply calls to static of Math • we have used static methods in other classes before: • Character.isLetter( c ) • Look up the API for these classes on the Java site!
Wrapper Classes • Methods can only be called for instances (or classes, if the moethod is static) • Data Values (e.g. int x=931) are not instances. • You cannot define a method for a data type. • Every data type in Java has a “Wrapper Class” • The wrapper class contains methods and constants for the corresponding data type.
Wrapper Classes • Some valid wrapper classes are: • Integer • Float • Double • Character • Boolean • You find their definition in the API specification. • Examples in the class Integer • Integer.MAX_VALUE is a static constant • Integer.parseInt(String s) is a static method it extracts an integer number from a string
Main method • A special static method is the “main” method. • Any class can have a main method. It must • be public • be static • have return type void • have a single parameter of type String[] This turns this class into an executable stand-alone program.
Compiling Executables (optional) public class Test { public static void main(String[] args) { System.out.println("Hello World! "); } Compile this class in BlueJ Find the file “Test.class” that BlueJ has generated Open a terminal window, go to the same directory and execute the program by typing: java -cp . Test
Compiling Executables (optional) public class Test { public static void main(String[] args) { System.out.println("Hello World! "); } Step 3 invokes the java runtime system (“java”), instructs it to search for classes in the current directory (“-cp .”) and invoke the main method for the class “Test”.
Compiling Executables (optional) public class Test { public static void main(String[] args) { System.out.println("Hello World! "); for (String s : args) System.out.println(s); } The parameter (String[] args) of main contain any further words on the command line, e.g. when called as java -cp . Test an old catargs would be an array of length 3 with the strings “an”, “old” and “cat”.The main method above prints these.
Variable Access from Static Methods • “A static method cannot access a non-static instance variable.” • this is not quite correct: A static method can only access instance variables of instances that it knows explicitly. • This should be obvious as there is no “executing” instance, there is no “this”.
Extending “Black-box” Classes • In OO programming you often just want to extend the functionality of a class that already exists. • In this case you treat the class that you extend as a “black box”. • You don’t need to know the internals of this class, just it’s API. • You then extend this class by defining a sub-class. • Example 1: To define a new type of account (“Mortgage”), we do not need to see the internals of Account, we can just sub-class it. • Example 2: Let us assume we want to extend the built-in class String so that it has a method to recognize Palindromes.
Extending “Black-box” Classes • public class Palindrome extends String { • public boolean isPalindrom() { • int len = this.length(); • boolean success = true; • for (int i=0; i<this.length()/2; i++) • success &= • (this.charAt(i)==this.charAt(len-i)); • return success; • } • }