680 likes | 852 Views
Debugging, Interfaces, Polymorphism. Debugging. Debugging is 75% Psychological 20% Logic 5% Caffeine Java is not broken Our code is not broken Your code is broken! This is where test mains payoff! Work backwards...liberal application of "System.out.printf". Mini-review.
E N D
Debugging • Debugging is • 75% Psychological • 20% Logic • 5% Caffeine • Java is not broken • Our code is not broken • Your code is broken! • This is where test mains payoff! • Work backwards...liberal application of "System.out.printf"
Mini-review • References: Pointers to objects. Include many of the classic confusing things about pointers! • Inheritence: defined with keyword "extends". • Subclasses inherit all of the fields and methods of the parent class • Define subclasses in their own file • Arrays are objects which require a reference Watch[] watchArray = new Watch[]; • For the most part avoid static • Creating a (reference variable vs creating an instance) TetrisBlock tb; tb = new TetrisBlock();
Animal Pet Dog Recall Multiple Inheritance Some languages allow multiple inheritance: • Multiple inheritance leads to many potentially confusing naming problems (e.g. Pet.doYourThing() vs. Animal.doYourThing()) • Growing consensus: the benefits of multiple inheritance aren’t worth the problems. • Java has single inheritance only. • Java doesn’t allow multiple inheritance • Well, there is a restricted kind that avoids most of the problems. It involves using interfaces.
We’ve already seen one use of interfaces public class P1Constants { public static final int FEETPERMILE = 5280; } public class SomeOtherClass { ... public int convertFeet2Miles(int feet) { return feet * P1Constants.FEETPERMILE; } ... }
We’ve already seen one use of interfaces public interface P1Constants { final int FEETPERMILE = 5280; } public class SomeOtherClass implements P1Constants { ... public int convertFeet2Miles(int feet) { return feet * FEETPERMILE; } ... }
But the key feature... I have this structure Animal Aquatic LandBased Goldfish Dog Cat Shark Giraffe But I want some of the animals to be Pets?
Pets will have names! public interface Pet { public String getName(); public void setName(String name); } Note: We don't define the variable name
So now if I want a dog to be a pet: class Dog implements Pet { private String name; /* Plus other fields */ public String getName() { return name; } public void setName(String name) { this.name = name; } /* Plus other methods */ }
Questions? Visibility and Access Can an object use a field or call a method? Visibility Modifier: Access by: public protected private Every class Yes No*No A subclass Yes Yes No An instance of the class Yes Yes Yes Always specify a visibility modifier. Guidelines: Only make public methods that are in the class’s “contract” Make all fields private Make all other “private” methods protected Don’t leave off the modifier unless you know about packages
In Our Last Episode... Recall last lecture’s discussion of polymorphism. This concept is a KEY feature of any object oriented language, not just Java. It is imperative that you understand how it works. Let us review
Recall On the 7th Lecture, we created many animals . . . ... and it was good. class Animal { public void move ( ) { System.out.println (“I am an animal and am moving.”); } } // of Animal class Fish extends Animal { public void move( ) { System.out.println (“Swim, swim, swim, swim!”); } } // of Fish class Bird extends Animal { public void move( ) { System.out.println (“Flap, flap, flap again”); } } // of Bird class Dog extends Animal { public void move ( ) { System.out.println (“Run, scratch, walk, run”); } public void bark ( ){ System.out.println (“Arf Arf”); } } // of Dog
Recall We also noted that we could use polymorphism to hold an array of animals, and employ dynamic binding to invoke specific behaviors. class Driver { public static void main (String[ ] argv) { Animal[ ] animalArray = new Animal[3]; int iIndex; animalArray[0] = new Bird( ); animalArray[1] = new Dog( ); animalArray[2] = new Fish( ); for (iIndex=0; iIndex < animalArray.length; iIndex++) { animalArray[iIndex].move( ); } // of for } // of main } // of Driver Output: Flap, flap, flap again Run, scratch, walk, run Swim, swim, swim, swim!
And so... This time, we have instance and class variables. The int total is a population counter. The int numLegs records the number of legs for the animal. The String strType describes the animal. Let’s review the creation of an object, using a slightly more complex class example This may not be the best design. Don’t forget this is an academic exercise. We’re doing this for a reason. class Animal { public int numLegs; public static int total = 0; public String strType; public Animal (int numLegs){ this.numLegs = numLegs; strType = “Animal”; total++; } public void move ( ) { System.out.println (“I am an animal and am moving.”); } public String toString(){ return strType + “ with “ + numLegs + “ legs.”; } } // of Animal
And so... The move() method is the same as before. We’ve added a constructor and a simple toString(). In a perfect world, we’d have room on the slides for the accessors and modifiers. Let’s review the creation of an object, using a slightly more complex class example class Animal { public int numLegs; public static int total = 0; private String strType; public Animal (int numLegs){ this.numLegs = numLegs; strType = “Animal”; total++; } public void move ( ) { System.out.println (“I am an animal and am moving.”); } public String toString(){ return strType + “ with “ + numLegs + “legs.”; } } // of Animal
And also... class Fish extends Animal { public Fish(int numLegs){ super(numLegs); strType = “Fish”; } public void move( ) { System.out.println (“Swim, swim, swim, swim!”); } } // of Fish The Fish class is very much the same. We just add a new constructor, which invokes the parent constructor, and then corrects the strType assignment.
And also... class Bird extends Animal { public Bird(int numLegs){ super (numLegs); strType = “Bird”; } public void move( ) { System.out.println (“Flap, flap, flap again”); } } // of Bird Same story with the Bird class. We added a new constructor, and set the type appropriately.
And also... class Dog extends Animal { public Dog(int numLegs){ super (numLegs); strType = “Dog”; } public void move ( ) { System.out.println (“Run, scratch, walk, run”); } public void bark ( ){ System.out.println (“Arf Arf”); } } // of Dog Same story with the Dog class. We added a new constructor, and set the type appropriately.
And Finally... class Driver { public static void main (String[ ] argv) { Animal[ ] animalArray = new Animal[3]; int iIndex; animalArray[0] = new Bird(2); animalArray[1] = new Dog(3); animalArray[2] = new Fish(0); for (i=0; i < animalArray.length; i++) { System.out.println (animalArray[i].toString()); animalArray[i].move( ); } // of for System.out.println (“Total Count: “ + Animal.total); } // of main } // of Driver We also revise our Driver class, passing in appropriate values to the constructors. (We happen to have a three-legged dog.)
What Happens? class Animal { public int numLegs; public static int total = 0; private String strType; public Animal (int numLegs){ this.numLegs = numLegs; strType = “Animal”; total++; } public void move ( ) { System.out.println (“I am an animal and am moving.”); } public String toString(){ return strType + “ with “ + numLegs + “ legs.”; } } // of Animal class Bird extends Animal { public Bird(int numLegs){ super (numLegs); strType = “Bird”; } public void move( ) { System.out.println (“Flap, flap, flap again”); } } // of Bird class Fish extends Animal { public Fish(int numLegs){ super(numLegs); strType = “Fish”; } public void move( ) { System.out.println (“Swim, swim, swim, swim!”); } } // of Fish class Dog extends Animal { public Dog(int numLegs){ super (numLegs); strType = “Dog”; } public void move ( ) { System.out.println (“Run, scratch, walk, run”); } public void bark ( ){ System.out.println (“Arf Arf”); } } // of Dog class Driver { public static void main (String[ ] argv) { Animal[ ] animalArray = new Animal[3]; int iIndex; animalArray[0] = new Bird(2); animalArray[1] = new Dog(3); animalArray[2] = new Fish(0); for (i=0; i < animalArray.length; i++) { System.out.println (animalArray[i].toString()); animalArray[i].move( ); } // of for } // of main } // of Driver If we run the Driver, what happens? Moreover, WHY does it happen?
A Look At Memory class Animal { public int numLegs; public static int total; private String strType; public Animal (int numLegs){ this.numLegs = numLegs; strType = “Animal”; } public void move ( ) { System.out.println (“I am an animal and am moving.”); } public String toString(){ return strType + “ with “ + numLegs + “ legs.”; } } // of Animal We intend to take a close look at these objects in memory. But before we do that, we have to condense what we’ve written. We’ll need to agree on some symbols and a notation system. Animal int numLegs String strType toString(); move(); We’ll represent Animal instances in memory as a box. We’ll have a little room for the variables, and a few key methods.
A Look At Memory class Animal { public int numLegs; public static int total; private String strType; public Animal (int numLegs){ this.numLegs = numLegs; strType = “Animal”; } public void move ( ) { System.out.println (“I am an animal and am moving.”); } public String toString(){ return strType + “ with “ + numLegs + “ legs.”; } } // of Animal We intend to take a close look at these objects in memory. But before we do that, we have to condense what we’ve written. We’ll need to agree on some symbols and a notation system. Object toString() In future classes, you’ll learn about UML diagrams. But for now, it’s all boxes and arrows. Animal int numLegs String strType toString(); move(); Since an Animal inherits from Object (remember IS-A relationships are specified by inheritance), we also draw an Object box.
A Look At Memory class Animal { public int numLegs; public static int total; private String strType; public Animal (int numLegs){ this.numLegs = numLegs; strType = “Animal”; } public void move ( ) { System.out.println (“I am an animal and am moving.”); } public String toString(){ return strType + “ with “ + numLegs + “ legs.”; } } // of Animal class Fish extends Animal { public Fish(int numLegs){ super(numLegs); strType = “Fish”; } public void move( ) { System.out.println (“Swim, swim, swim, swim!”); } } // of Fish Now, a Fish is more complex. It inherits from Animal which extends Object. Object This whole area is a Fish object in memory. Since a Fish IS-A Animal, and an Animal IS-A Object, we draw all three boxes to express one object toString() Animal int numLegs String strType toString(); move(); Fish move();
A Look At Memory Thus, if we make a Fish instance, we have a reference pointing to this block of memory that we’ve drawn. Object Fish fTemp; fTemp = new Fish(0); toString() Animal int numLegs = 0 String strType toString(); move(); Fish move(); fTemp By convention, we will have the reference point to a particular portion of the block of memory, even though the entire block is one object. Since our reference is a Fish type, our arrow enters at the Fish portion of the object in memory.
Recall Our Object Creation Timeline Object Object Object toString() toString() toString() Animal Animal Animal int numLegs = 0 String strType toString(); move(); int numLegs = 0 String strType toString(); move(); int numLegs = 0 String strType toString(); move(); Fish Fish Fish move(); move(); move(); fTemp Remember that a call to the Fish(int) constructor involves an explicit Animal(int) constructor call. Even if we did not have this explicit call, Java would implicitly call the parent class constructor, just as it implicitly makes an Object when the Animal(int) constructor is invoked from the Fish(int) constructor. 2 3 1 Fish fTemp = new Fish(0); To make a Fish, Java must first make an Animal. To make an Animal, Java must first make an Object.
Wow. Notice something else. Since all Fish are Object types, we can have an Object reference pointing to this block of memory. Object oTemp; oTemp = new Fish(0); Object toString() Animal int numLegs = 0 String strType toString(); move(); A single Fish instance Fish move(); oTemp We adjust where the reference enters the block of memory, that that it reflects the reference type.
Ahh. Object toString() Animal int numLegs = 0 String strType toString(); move(); Fish move(); So we can just make new reference types and have them point into our block at a different locations. By ‘twiddling’ the reference into our block of memory, we can polymorph the object. Animal aTemp; aTemp = new Fish(0); aTemp We adjust where reference points, so that it reflects its type.
Hey, Cool Object toString() Animal int numLegs = 3 String strType toString(); move(); Fish Fish move(); ref That’s a simple example of polymorphism.
Hey, Cool Object toString() Animal int numLegs = 3 String strType toString(); move(); Animal Fish move(); ref That’s a simple example of polymorphism.
Hey, Cool It’s the same object in memory. We just change the reference type pointing to it. Object toString() Animal int numLegs = 3 String strType toString(); move(); Object Fish move(); ref That’s a simple example of polymorphism.
Polymorphism Many forms One object Object toString() Animal int numLegs = 3 String strType toString(); move(); <type> Fish move(); ref Literally, polymorphism means “Many forms” So for us we take it to imply, "One object; many forms”
Thus Object Object Object toString() toString() toString() Animal Animal Animal int numLegs = 3 String strType toString(); move(); int numLegs = 0 String strType toString(); move(); int numLegs = 2 String strType toString(); move(); Dog Fish Bird move(); bark(); move(); move(); So we have three box groupings that represent arbitrary instances of Fish, Dog and Bird, pointed to by any appropriate reference type. fTemp dTemp bTemp Fish fTemp = new Fish(0); Dog dTemp = new Dog(3); Bird bTemp = new Bird(2);
Recall class Animal { public int numLegs; public static int total = 0; private String strType; public Animal (int numLegs){ this.numLegs = numLegs; strType = “Animal”; total++; } public void move ( ) { System.out.println (“I am an animal and am moving.”); } public String toString(){ return strType + “ with “ + numLegs + “ legs.”; } } // of Animal class Bird extends Animal { public Bird(int numLegs){ super (numLegs); strType = “Bird”; } public void move( ) { System.out.println (“Flap, flap, flap again”); } } // of Bird class Fish extends Animal { public Fish(int numLegs){ super(numLegs); strType = “Fish”; } public void move( ) { System.out.println (“Swim, swim, swim, swim!”); } } // of Fish class Dog extends Animal { public Dog(int numLegs){ super (numLegs); strType = “Dog”; } public void move ( ) { System.out.println (“Run, scratch, walk, run”); } public void bark ( ){ System.out.println (“Arf Arf”); } } // of Dog So we figured out how to represent inheritance in memory. Now recall that we also had our classes point to a String literal describing the type. A String literal is, well, literally a String. It’s something in quotes in your source code. Literally what’s there.
The Constant Pool Object toString() Animal int numLegs = 2 String strType toString(); move(); Bird move(); Constant Pool Animal aTemp; aTemp = new Bird(2); “Animal” “Fish” “Bird” “Dog” aTemp As it turns out, there’s an area of the Java Virtual Machine called the Constant Pool. It’s a special area to hold declared constants (final variables), since they cannot be changed. As it turns out, this is where String literals are placed.
The Constant Pool Object Constant Pool toString() “Animal” “Fish” “Bird” “Dog” Animal int numLegs = 2 String strType toString(); move(); Bird move(); Animal aTemp; aTemp = new Bird(2); aTemp Since Strings are Objects, and are addressed through references, the strType variable in the Animal class points to one of these literals in the Constant Pool.
The Constant Pool Object Constant Pool toString() “Animal” “Fish” “Bird” “Dog” Animal int numLegs = 2 String strType toString(); move(); Bird move(); CONTRA Animal aTemp; aTemp = new Bird(2); aTemp Note that this is different for primitive instance variables. Primitives are not references. So we just write the value right next to the field
In Fact... Object String Object String Object String String Object Object “Animal” toString(); toString(); “Dog” toString(); toString() “Bird” toString(); “Fish” Animal int numLegs = 2 String strType toString(); move(); Bird move(); Animal aTemp; aTemp = new Bird(2); Constant Pool aTemp But this is getting too complicated. In fact, the String strType also inherits from Object. So we could even draw the Constant Pool as a collection of objects with inheritance.
What would Keanu Reeves say? “Whoa.”
Object Object Object toString() toString() toString() So, we’ll abstract away most of the details, and just note that objects in Java are handled through references. Animal int numLegs = 3 String strType toString(); move(); Animal int numLegs = 2 String strType toString(); move(); Fish Constant Pool move(); Bird “Animal” “Fish” “Bird” “Dog” move(); Animal int numLegs = 0 String strType toString(); move(); All of the objects created by our Driver class have unique values (numLegs) and unique references. Dog move(); bark();
Object Object toString() toString() String <whatever IOHelper returns> Note also that if we assign the String strType to point to something else, it might not reference the Constant Pool. Animal int numLegs = 2 String strType toString(); move(); Bird move(); Constant Pool “Animal” “Fish” “Bird” “Dog” Operations like new String(); substring(); and IOHelper.readLine(); cause the allocation of new String objects Bird bTemp = new Bird(0); bTemp.setType (IOHelper.readLine());
What About Non-Instance Data? Object Object toString() toString() Animal Animal int numLegs = 0 String strType toString(); move(); int numLegs = 3 String strType toString(); move(); Dog Fish move(); bark(); move(); The int numLegs was an instance variable of the class Animal, and each instance had its own copy. But the variable int total--the population counter--was declared static. Thus, it was a classvariable. How can we account for this in our diagrams?
What About Non-Instance Data? Object Object class Animal { public int numLegs; public static int total = 0; private String strType; public Animal (int numLegs){ this.numLegs = numLegs; strType = “Animal”; total++; } public void move ( ) { System.out.println (“I am an animal and am moving.”); } public String toString(){ return strType + “ with “ + numLegs + “ legs.”; } } // of Animal toString() toString() Animal Animal int numLegs = 0 String strType toString(); move(); int numLegs = 3 String strType toString(); move(); Dog Fish move(); bark(); move(); Class Pool It just so happens there is something called the Class Pool in Java, which holds information about each class used in your program. You can think of this as the “text” area where your code is kept. The other parts of memory are the “data” area where objects are kept. The Class Pool is like a country club. You have to have some class to be admitted.
class Animal { public int numLegs; public static int total = 0; private String strType; public Animal (int numLegs){ this.numLegs = numLegs; strType = “Animal”; total++; } public void move ( ) { System.out.println (“I am an animal and am moving.”); } public String toString(){ return strType + “ with “ + numLegs + “ legs.”; } } // of Animal class Bird extends Animal { public Bird(int numLegs){ super (numLegs); strType = “Bird”; } public void move( ) { System.out.println (“Flap, flap, flap again”); } } // of Bird class Fish extends Animal { public Fish(int numLegs){ super(numLegs); strType = “Fish”; } public void move( ) { System.out.println (“Swim, swim, swim, swim!”); } } // of Fish class Dog extends Animal { public Dog(int numLegs){ super (numLegs); strType = “Dog”; } public void move ( ) { System.out.println (“Run, scratch, walk, run”); } public void bark ( ){ System.out.println (“Arf Arf”); } } // of Dog class Driver { public static void main (String[ ] argv) { Animal[ ] animalArray = new Animal[3]; int iIndex; animalArray[0] = new Bird(2); animalArray[1] = new Dog(3); animalArray[2] = new Fish(0); for (i=0; i < animalArray.length; i++) { System.out.println (animalArray[i].toString()); animalArray[i].move( ); } // of for } // of main } // of Driver (In class Animal) static int total: 3 As it turns out, the Class Pool is enormous (probably Olympic size). It holds not only your classes, but all the classes used by Java--String, Object, Integer, etc. Every class used to run your program gets loaded here. Java uses the Class Pool to keep track of the working code and methods in each class. Since static variables are “of the class” and not “of the instance”, they are kept as part of this storage device. Class Pool
class Animal { public int numLegs; public static int total = 0; private String strType; public Animal (int numLegs){ this.numLegs = numLegs; strType = “Animal”; total++; } public void move ( ) { System.out.println (“I am an animal and am moving.”); } public String toString(){ return strType + “ with “ + numLegs + “ legs.”; } } // of Animal class Bird extends Animal { public Bird(int numLegs){ super (numLegs); strType = “Bird”; } public void move( ) { System.out.println (“Flap, flap, flap again”); } } // of Bird class Fish extends Animal { public Fish(int numLegs){ super(numLegs); strType = “Fish”; } public void move( ) { System.out.println (“Swim, swim, swim, swim!”); } } // of Fish class Dog extends Animal { public Dog(int numLegs){ super (numLegs); strType = “Dog”; } public void move ( ) { System.out.println (“Run, scratch, walk, run”); } public void bark ( ){ System.out.println (“Arf Arf”); } } // of Dog class Driver { public static void main (String[ ] argv) { Animal[ ] animalArray = new Animal[3]; int iIndex; animalArray[0] = new Bird(2); animalArray[1] = new Dog(3); animalArray[2] = new Fish(0); for (i=0; i < animalArray.length; i++) { System.out.println (animalArray[i].toString()); animalArray[i].move( ); } // of for } // of main } // of Driver (In class Animal) static int total: 3 That’s why we can use the class name to invoke class methods, or access class variables. When we access instance variables, however, we must work through an instance of the class. Class Pool int x = Animal.total; int leg = myDog.getLegCount(); (This assumes we have an accessor; there’s not enough room on these slides.)
Style Point: You can also access class members (static stuff) using instance references. Integer iTemp = new Integer(3); String strNumber = “42”; int x = iTemp.parseInt(strNumber); // OK int y = Integer.parseInt (strNumber); // better There is, however, a convention, of using class names to access class methods/fields. Using an instance of the class also works; however, it gives the impression that it must be done through an instance. So, (1) Use what works. (2) But use what’s clear.
(Please take a moment to check that your socks were not knocked off during the previous slides...)