COE318 Software Systems Lecture Notes: Week 8 1 of 17 COE318 Lecture Notes Week 8 (Oct 24, 2011) Topics == vs..equals(...): A first look Casting Inheritance, interfaces, etc Introduction to Juni (unit testing) Vocabulary There are new words and concepts to learn this week including: inheritance (subclassing) subclass superclass extends implements Exception try catch casting abstract interface overrides overloads super(...) this(...) super.foo(...) instanceof marker interface
COE318 Software Systems Lecture Notes: Week 8 2 of 17 polymorphism == vs. equals(...) For primitive data types (int, double, boolean, char, etc.) the only way to test if two values are equal is with ==; thus if i and j are ints then (i == j) is true if and only if they both have the same value. For reference data types, however, you can test for different types of equality : It is legal (but usually wrong) to use == just as with primitive types. Two reference objects are equal in this sense only if they refer to the identical object. The equals(...) method is almost always the correct way to check for equality of two reference variables. Consider the example below (where we assume a ComplexNukber class similar to what you did in the lab except that it has an equals() method that returns true if both complex numbers have exactly the same real and imaginary parts.) ComplexNumber w, y, z; w = new ComplexNumber(1, 2); z = new ComplexNumber(1, 2); y = z; //Now w == z is false; they are different objects! //But w.equals(z) is true since they represent the same //complex number. //Both y == z and y.equals(z) are true since they are //the same object. Casts primitive data types come in different sizses; an int is a 32-bit signed integer; a short is 16-bits; a byte is 8 bits and a long is 64 bits; When a smaller type is assigned to a larger type, a cast is required. This kind of primitive data casting is the same as in C. In the following example, all the casts are necessary (or there will be compilation errors.) We will consider casting of reference type data later on. public class Casts { public static void main(string[] args) { byte b = 1; short s = 1;
COE318 Software Systems Lecture Notes: Week 8 3 of 17 int i = 0x7fff0005; s = (short) i; System.out.println("s: " + s); System.out.println("i: " + i); double d = 2.99; i = (int) d; System.out.println("i: " + i); s = -1; char ch = (char) s; i = s; System.out.println("i: " + i); i = ch; System.out.println("i: " + i); By the way, the output from this program is: s: 5 i: 2147418117 i: 2 i: -1 i: 65535 Can you see why this is the output? (Hint: when something is too big for a smaller number of bytes, it is truncated. Also, all numbers in Java are signed except for char which is 16-bit unsigned (short is 16-bit signed.)) Will be explained in next lecure. Zoo: Version 1 This simple version models a Zoo that contains an arbitrary number of Lions. There is nothing new here. It's just a starting point for exploring, using and understanding some important features of object-oriented programming. But, some notes... The instance variables of Lion are private and final. Consequently, Lion objects are (for the moment) immutable. public class Lion {
COE318 Software Systems Lecture Notes: Week 8 4 of 17 private final String name; private final int birthyear; public Lion(String name, int year) { this.name = name; birthyear = year; public int age() { return 2011 - birthyear; public String getname() { return name; public String noise() { return "GGGrowl"; public boolean eatspeople() { return true; import java.util.arraylist; public class Zoo { private final ArrayList<Lion> lions = new ArrayList<Lion>(); public void add(lion lion) { lions.add(lion); public String getnames() { String s = ""; for(lion leo : lions) { s += leo.getname() + "\n";
COE318 Software Systems Lecture Notes: Week 8 5 of 17 return s; public static void main(string[] args) { Zoo zoo = new Zoo(); Lion a = new Lion("Alex", 2010); zoo.add(a); zoo.add(new Lion("Betty", 2009)); System.out.println(zoo.getNames()); Zoo Version 2: A First Look at Inheritance The zoo wants to have more animals than just lions. But proceeding as before would be very tedious if, say, there were 100 different kinds of animals: We'd need to create Pig, Cat, Crocodile, Elephant, Hummingbird, etc. classes and they would all have just about the same code; We'd need separate ArrayLists for each type of animal in the Zoo class. It would be difficult to treat all the animals as a group: to sort them alphabetically by name or by age, for example. Inheritance (subclassing) to the rescue! We can put practically all the code for Lion into another class we call AbstractAnimal. (Why not call it Animal instead of AbstractAnimal? Good question! To be explained in the next version.) We can then rewrite the Lion class to extend the AbstractAnimal class; all of its methods are inherited by Lion and do not have to be copied and pasted. The Lion class now looks like: public class Lion extends AbstractAnimal { public Lion(String name, int year) { super(name, year);
COE318 Software Systems Lecture Notes: Week 8 6 of 17 But what's this super(name, year); stuff? It invokes the constructor (with the same signature) of its superclass. It is not always necessary to invoke the superclass constructor as is done here; however, If your do, it must be the first statement in the subclass's constructor. The superclass is AbstractAnimal and here's what it looks like: public class AbstractAnimal { private final String name; private final int birthyear; public AbstractAnimal(String name, int year) { this.name = name; birthyear = year; public int age() { return 2011 - birthyear; public String getname() { return name; public String noise() { return "GGGrowl"; public boolean eatspeople() { return true; We can now write the Hedgehog class. However, the inherited methods eatspeople() and noise() give the wrong answer for a hedgehog. An inherited method can be overridden by simply re-writing it. (A subclass almost always overrides at least one method from its superclass.)
COE318 Software Systems Lecture Notes: Week 8 7 of 17 Note: The line is not required. It can be useful for the compiler and some tools. Netbeans recommends you put it in. It also reminds programmers that they are modifying an inherited method.) In general, lines beginning with @ are called annotations. Programmers can define their own annotations but that is beyond the scope of this course.) public class Hedgehog extends AbstractAnimal { public Hedgehog(String name, int year) { super(name, year); public boolean eatspeople() { return false; How do we declare the data type of a Lion or Hedgehog? In the past (before inheritance), this question would appear foolish. For example, we would write things like: Resistor r; r = new Resistor(10); r.setvoltage(10); There would be no other choice. But with inheritance we can declare a Lion as either of type Lion or type AbstractAnimal: Lion p = new Lion( Leo ); AbstractAnimal q = new Lion( Simba ); There is absolutely no difference between how p and q behave. They are both Lions and behave as such no matter how their data types were declared. And there is no difference in runtime efficiency. But declaring Lions or Hedgehogs or Pigs to be AbstractAnimals has big advantages. For example, the Zoo class can keep all of the animals in a single ArrayList<AbstractAnimal>. Below is the new, improved Zoo class: import java.util.arraylist;
COE318 Software Systems Lecture Notes: Week 8 8 of 17 public class Zoo { private final ArrayList<AbstractAnimal> animals = new ArrayList<AbstractAnimal>(); public void add(abstractanimal animal) { animals.add(animal); public String getnames() { String s = ""; for (AbstractAnimal a : animals) { s += a.getname() + "\n"; return s; public AbstractAnimal[] getanimals() { AbstractAnimal[] ts = new AbstractAnimal[0]; return animals.toarray(ts); public static void main(string[] args) { Zoo zoo = new Zoo(); Lion a = new Lion("Leo", 2010); zoo.add(a); zoo.add(new Lion("Simba", 2009)); zoo.add(new Hedgehog("Sonic", 2010)); System.out.println(zoo.getNames()); AbstractAnimal[] animals = zoo.getanimals(); for (AbstractAnimal an : animals) { System.out.println(an.getClass().getName() + " " + an.getname() + ": " + (an.eatspeople()? "eats" : "does not eat") + " people");
COE318 Software Systems Lecture Notes: Week 8 9 of 17 Zoo Version 3: Abstract classes and methods AbstractAnimal is just a normal class and it is legal to write: AbstractAnimal a = new AbstractAnimal( Sue ); This should not be allowed! It makes no sense to create an animal when we don't know what kind of animal it is. AbstractAnimal exists only for the purpose of subclassing. We can make this clear by declaring it to be abstract. The compiler will now disallow: AbstractAnimal a = new AbstractAnimal( Sue ); We now refer to AbstractAnimal as an abstract class that cannot be instantiated. But Lion and Hedgehog are concrete classes that can be instantiated. We can also declare methods to be abstract. If we do so, we must declare the class to be abstract. Furthermore, when we extend an abstract class as a concrete class, the compiler will insist that we write implementation code for any abstract methods. The code for our classes is given below. Note: the Zoo class needs no changes. The AbstractAnimal class is now declared abstract as is its noise() method. Both Lion and Hedgehog now have to implement (and not just inherit) the noise() method. public abstract class AbstractAnimal { private final String name; private final int birthyear; public AbstractAnimal(String name, int year) { this.name = name; birthyear = year; public int age() { return 2011 - birthyear; public String getname() { return name;
COE318 Software Systems Lecture Notes: Week 8 10 of 17 public abstract String noise(); public boolean eatspeople() { return true; public class Hedgehog extends AbstractAnimal { public Hedgehog(String name, int year) { super(name, year); public boolean eatspeople() { return false; public String noise() { return "Chirp...chirp"; public class Lion extends AbstractAnimal { public Lion(String name, int year) { super(name, year); public String noise() { return "Rrroar (yum yum)";
COE318 Software Systems Lecture Notes: Week 8 11 of 17 Zoo Version 4: Interfaces The zoo owner is happy with version 3; lots and lots of concrete animal classes can be easily added; no changes needed to the Zoo or AbstractAnimal classes. But then one day he says, I love my fridge. I want to have it as one of the amimals in my zoo. The guy may be a lunatic, but we have to respect his wishes. Tell him just to subclass Refrigerator from AbstractAnimal. But he can't do that; Refrigerator is already subclassed from ElectricalAppliance. OK, we'll make an interface called Animal. He just has to implement the interface. What is an interface? An interface is simply a collection of zero or more public abstract methods. A class can implement as many interfaces as it wants. It just has to ensure that all the abstract methods are actually implemented. (And the compiler will ensure that this is done.) We also take advantage of the Animal interface and have AbstractAnimal implement it. Objects can be declared as their own type or as any of their superclass's type. In addition, an object can be declared as any interface type implemented by the actual class. Consequently, we can now declare all the animals (and the fridge) as type Animal. We also have AbstractAnimal implement a standard imterface, Comparable<Animal>. Note (lab 5): You are provided with an interface in lab 5: UserInterface which describes what the methods should do. The reason for this so that any user interface has the same mthods and can be easily plugged into the existing code. (A graphical user interface would have buttons and show the cards in a graphical way. A networked interface coudl also be written.) interface Animal { String noise(); boolean eatspeople(); int age(); String getname(); public abstract class AbstractAnimal implements Animal, Comparable<Animal> { private final String name; private final int birthyear;
COE318 Software Systems Lecture Notes: Week 8 12 of 17 public AbstractAnimal(String name, int year) { this.name = name; birthyear = year; public int age() { return 2011 - birthyear; public String getname() { return name; public abstract String noise(); public boolean eatspeople() { return this instanceof Dangerous; public int compareto(animal other) { return getname().compareto(other.getname()); public boolean equals(object obj) { if (obj == null) { return false; if (getclass()!= obj.getclass()) { return false; final AbstractAnimal other = (AbstractAnimal) obj; if ((this.name == null)? (other.name!= null) :!this.name.equals(other.name)) { return false; return true;
COE318 Software Systems Lecture Notes: Week 8 13 of 17 public int hashcode() { int hash = 7; hash = 83 * hash + (this.name!= null? this.name.hashcode() : 0); return hash; public interface Dangerous { public class Lion extends AbstractAnimal implements Dangerous{ public Lion(String name, int year) { super(name, year); public String noise() { return "GGGrowl"; public class Hedgehog extends AbstractAnimal { public Hedgehog(String name, int year) { super(name, year);
COE318 Software Systems Lecture Notes: Week 8 14 of 17 public String noise() { return "Chirp chirp"; import java.util.arraylist; public class Zoo { private final ArrayList<Animal> animals = new ArrayList<Animal>(); public void add(animal animal) { animals.add(animal); public String getnames() { String s = ""; for (Animal a : animals) { s += a.getname() + "\n"; return s; public Animal[] getanimals() { Animal[] ts = new AbstractAnimal[0]; return animals.toarray(ts); public static void main(string[] args) { Zoo zoo = new Zoo(); Animal a = new Lion("Alex", 2010); zoo.add(a); zoo.add(new Lion("Betty", 2009)); zoo.add(new Hedgehog("Charlie", 2010)); System.out.println(zoo.getNames()); Animal[] animals = zoo.getanimals(); for (Animal an : animals) { System.out.println(an.getClass().getName() + " " + an.getname() + ": " + (an.eatspeople()? "eats" : "does not eat")
COE318 Software Systems Lecture Notes: Week 8 15 of 17 + " people"); Zoo Version 5: Exceptions import java.util.arraylist; public class Zoo { private final ArrayList<Animal> animals = new ArrayList<Animal>(); public void add(animal animal) { if (animals.contains(animal)) { throw new IllegalArgumentException("Duplicate name not allowed"); animals.add(animal); public String getnames() { String s = ""; for (Animal a : animals) { s += a.getname() + "\n"; return s; public Animal[] getanimals() { Animal[] ts = new AbstractAnimal[0]; return animals.toarray(ts); public static void main(string[] args) {
COE318 Software Systems Lecture Notes: Week 8 16 of 17 Zoo zoo = new Zoo(); Animal a = new Lion("Alex", 2010); zoo.add(a); zoo.add(new Lion("Betty", 2009)); Animal charlie1 = new Hedgehog("Charlie", 2010); zoo.add(charlie1); System.out.println(zoo.getNames()); Animal charlie2 = new Lion("Charlie", 2007); try { zoo.add(charlie2); catch (IllegalArgumentException ex) { System.err.println("Duplicate name not added"); Animal[] animals = zoo.getanimals(); for (Animal an : animals) { System.out.println(an.getClass().getName() + " " + an.getname() + ": " + (an.eatspeople()? "eats" : "does not eat") + " people"); Unit testing and Junit Unit testing (widely used in the software industry) allows you to write test methods independently for the implementation code. Netbeans supports a framefork for doing this with a prodcut called Junit. (We use version 4.) Test methods are annotated with @Test and must be public void. Suppose we have a class called Resistor (as in Lab 1). We could write a test method to verify that getvoltage() worked:
COE318 Software Systems Lecture Notes: Week 8 17 of 17 Questions @Test public void getvoltage() { Resistor r = new Resistor(10); r.setcurrent(30.); assertequals(300.0, r.getvoltage()); 1. Write an equals(object ob) for the ComplexNumber class. Answers 1. Just: public boolean equals(object ob) { if (ob == null) return false; if (!(ob instanceof ComplexNumber)) return false; ComplexNumber c = (ComplexNumber) ob; return (c.getreal() == getreal()) && (c.getimag() == getimag());