Oop
Oop
Oop
PJ Dillon CS401
Instance Variables
Lets look again at StringBuffer
Instance Variables
These are the data values within an object
Used to store the objects information
As we said previously, when using data abstraction we dont need to know explicitly what these are in order to use a class For example, look at the API for StringBuffer
Note that the instance variables are not even shown there
In actuality it is a variable-length array with a counter to keep track of how many locations are being used and is actually inherited from AbstractStringBuilder
See source in StringBuffer.java and AbstractStringBuilder.java cool!!!
Instance Variables
Many instance variables are declared with the keyword private
This means that they cannot be directly accessed outside the class itself Instance variables are typically declared to be private, based on the data abstraction that we discussed earlier
Recall that we do not need to know how the data is represented in order to use the type Therefore why even allow us to see it?
Now, however we WILL associate methods with objects (as shown with Polygon) These methods are called instance methods because they are associated with individual instances (or objects) of a class
StringBuffer B = new StringBuffer(this is ); B.append(really fun stuff!); System.out.println(B.toString());
Inheritance
Alternatively, our NewClass can directly inherit the properties of OldClass
Just then need to add the new properties
Can still augment the inherited interface Semantically defines a logical relationship between the two objects
Inheritance and is a
We can understand this better by considering the is a idea
A subclass object is a superclass object However, some extra instance variables and methods may have been added and some other methods may have been changed
Inheritance and is a
Animal is a Bird is a is a Human Fish
Bird, Human and Fish are all Animals However, an Animal is not necessarily a Bird, Human or Fish
Extending Classes
Inheritance in Java is implemented by extending a class
public class NewClass extends OldClass {
We then continue the definition of NewClass as normal However, implicit in NewClass are all data and operations associated with OldClass
Even though we dont see them in the definition
Note that private declarations are STILL PART of subclasses, but they are not directly accessible from the subclass point of view
See SuperClass.java, SubClass.java and ex18.java
Inheritance Example
As another example
Compare MixedNumber class and MixedNumber2 class Both utilize the authors' RationalNumber class to do most of the "work" Both also have the same functionality, but MixedNumber uses composition and MixedNumber2 uses inheritance
Note simplicity of MixedNumber2 methods Read over the comments carefully! See RationalNumber.java, MixedNumber.java and MixedNumber2.java
Polymorphism
Idea of polymorphism
See internet definition:
On Google type definition polymorphism and see the results
This search works for many CS terms that you may be curious about
http://www.wordiq.com/definition/Polymorphism_%28computer_science%29
Generally, it allows us to mix methods and objects of different types in a consistent way
Method Overloading
This is called ad hoc polymorphism, or method overloading
In this case different methods within the same class or in a common hierarchy share the same name but have different method signatures (name + parameters)
public static float max(float a, float b) public static float max(float a, float b, float c) public static int max(int a, int b)
When a method is called, the call signature is matched to the correct method version
Note: This is done during program COMPILATION
Method Overloading
If an exact signature match is not possible, the one that is closest via widening of the values is used
Widening means that values of smaller types are cast into values of larger types Ex: int to long int to float float to double Fewer widenings provides a "closer" match
If two or more versions of the method are possible with the same amount of widening, the call is ambiguous, and a compilation error will result
See ex20.java Note: This type of polymorphism is not necessarily object-oriented can be done in non-object-oriented languages
Polymorphism
Subclassing Polymorphism
Sometimes called true polymorphism Consists basically of two ideas: 1) Method overriding
A method defined in a superclass is redefined in a subclass with an identical method signature Since the signatures are identical, rather than overloading the method, it is instead overriding the method
For subclass objects, the definition in the subclass replaces the version in the superclass
Polymorphism
2) Dynamic (or late) binding
The code executed for a method call is associated with the call during run-time The actual method executed is determined by the type of the object, not the type of the reference
Polymorphism
Ex. Each subclass overrides the move() method in its own way Animal [] A = new Animal[3]; A[0] = new Bird(); A[1] = new Person(); A[2] = new Fish(); for (int i = 0; i < A.length; i++) A[i].move(); References are all the same, but objects are not Method invoked is that associated with the OBJECT, NOT with the reference move()
move()
move()
Given a reference R of class C, only methods and instance variables that are defined (initially) in class C or ABOVE in the class hierarchy can be accessed through R
They still exist if defined in a subclass, but they are not accessible through R
The above is NOT legal, even though the method exists for class Fish. The reason is that the method is not visible from the references point of view (A is an Animal reference so it can only see the data and methods defined in class Animal)
System.out.println(((Fish) A).getWaterType());
This is ok, since we have now cast the reference to the Fish type, which CAN access the method
Object Methods
Weve already seen that every class automatically inherits from Object Class Object defines a set of methods that every class inherits
public String toString() public boolean equals() public int hashCode() protected Object clone() //well ignore this
Each of these forms a contract to which all objects must adhere Object has a default implementation for each of these methods
Unless our classes override them, they inherit this behavior May or may not be what our classes require
toString()
toString() returns a string representation of the object The string should be a concise but informative representation that is easy for a person to read RULE: All classes should override this method Default implementation from the Object class constructs a string like:
ClassName@30E50DA3 Name of the class, @ character, followed by the HashCode for the class
equals()
Indicates whether two objects are logically equal to each other
Ex. string1.equals(done)
Seems simple, but there is some subtlety here equals() must satisfy the definition of an equivalence relation
Reflexive Symmetric Transitive
The references must point to the same object for equals() to return true
Contract of equals()
equals() must be:
Reflexive: for any non-null reference variable, x:
x.equals(x) must be true
Consistent: for any two non-null references, x and y, mutliple calls to x.equals(y) must consistently return true or consistently return false unless the data stored in either x or y changes between calls to equals(). Safe: for non-null reference x and null reference y:
x.equals(y) must return false i.e. x.equals(null) must return false equals() should not generate a NullPointerException, or ClassCastException
Contract of equals()
Consider a class SubClass
Extends SuperClass
Contract of equals()
Now consider the super class, SuperClass
Has its own equals method
public boolean equals(Object o) { if(o == null) return false; SuperClass sup = (SuperClass) o; return name.equals(sup.name); }
hashCode()
Returns a integer index value for the object
Used by hashtables to store objects
Contract
Multiple calls to hashCode() during one execution of the program must return the same integer
Assuming no data contained in the object changes between calls
Composition or Inheritance
Caveats like those we just discussed arise often with Inheritance Also, In heritance permanently associates a superclass with our class at compile time
We can only inherit from a single class Composition allows our class the flexibility to wrap around different superclasses at run-time
Composition is generally preferred over Inheritance We loose polymorphism and dynamic binding with Composition though
In many cases, we need those capabilities We use abstract classes and Interfaces to help solve these problems
Exceptions in Java
Run-time errors happen
User enters incorrect input Resource is not available (ex. file) Logic error (bug) that was not fixed
Exceptions in Java
Exception:
An occurrence of an erroneous, unusual or unexpected event in a program execution In older languages
Code the handling of exceptions into each area of the program that needed it Some exceptions could not even be handled by the HLL
ex. standard Pascal cannot handle I/O errors or division by 0 Ask for integer and user enters a text string what do you do?
Exceptions in Java
In newer languages
Exception handling built into the language We can separate exception handling from the "main line" code
Exceptions in Java
Java exception handling
Exceptions are handled using try-catch blocks
try { // code that will normally execute } catch (ExceptionType1 e) { // code to "handle" this exception } catch (ExceptionType2 e) { // code to "handle" this exception } ... // can have many catches finally { // code to "clean up" before leaving try block }
Exceptions in Java
If all goes well (no exceptions occur)
Code in try block is executed, followed by code in (optional) finally block
Exceptions in Java
If an exception is handled
Execution resumes immediately AFTER try/catch block in which it was handled, and does NOT return to throw point termination model of exception handling
As opposed to a resumption model, where execution resumes from where the exception occurred
If an exception is propagated
A handler is searched for by backing up through the call chain on the run-time stack This is dynamic exception propagation If no handler is ever found
Console applications crash and report exception GUI applications will continue to execute, but may be in an inconsistent state more soon
Exceptions in Java
Checked vs. Unchecked exceptions
Checked exceptions
If a method does NOT handle these, the method MUST state that it throws them
Done in a throws clause in the method header
Unchecked exceptions
Method not required to explicitly "throw" these These include RunTimeException and Error
Exceptions in Java
Catching exceptions
Catching a superclass of an exception will catch subclass exception objects
catch (Exception e)
It is better style to be as specific as possible with the exceptions that are caught
See ex22.java
Abstract Classes
Abstract classes
Sometimes in a class hierarchy, a class may be defined simply to give cohesion to its subclasses
No objects of that class will ever be defined But instance data and methods will still be inherited by all subclasses
Abstract Classes
Subclasses of an abstract class must implement all abstract methods, or they too must be declared to be abstract Advantages
Can still use superclass reference to access all subclass objects in polymorphic way
However, we need to declare the methods we will need in the superclass, even if they are abstract
No need to specifically define common data and methods for each subclass - it is inherited Helps to organize class hierarchy
Interfaces
Java allows only single inheritance
A new class can be a subclass of only one parent (super) class There are several reasons for this, from both the implementation (i.e. how to do it in the compiler and interpreter) point of view and the programmer (i.e. how to use it effectively) point of view However, it is sometimes useful to be able to access an object through more than one superclass reference
Interfaces
We may want to identify an object in multiple ways:
One based on its inherent nature (i.e. its inheritance chain)
Ex: A Person
Interfaces
Any Java class (no matter what its inheritance) can implement an interface by implementing the methods defined in it A given class can implement any number of interfaces
Ex:
Interfaces
public interface Laughable { public void laugh(); } public interface Booable { public void boo(); }
Any Java class can implement Laughable by implementing the method laugh() Any Java class can implement Booable by implementing the method boo()
Interfaces
Ex:
public class Comedian implements Laughable, Booable { // various methods here (constructor, etc.) public void laugh() { System.out.println(Ha ha ha); } public void boo() { System.out.println(You stink!); } }
Interfaces
An interface variable can be used to reference any object that implements that interface
Note that the same method name (ex: laugh() below) may in fact represent different code segments in different classes Also, only the interface methods are accessible through the interface reference
Ex:
Laughable L1, L2, L3; L1 = new Comedian(); L2 = new SitCom(); // implements Laughable L3 = new Clown(); // implements Laughable L1.laugh(); L2.laugh(); L3.laugh();
Interfaces
Polymorphism and Dynamic Binding also apply to interfaces
the interface acts as a superclass and the implementing classes implement the actual methods however they want
An interface variable can be used to reference any object that implements that interface
However, only the interface methods are accessible through the interface reference
See ex24.java
"Generic" Operations
How does it benefit us to be able to access objects through interfaces?
Sometimes we are only concerned about a given property or behavior of a class
The other attributes and methods still exist, but we don't care about them for what we want to do
Generic Operations
They all can be compared to each other
So we need some method that invokes this comparison
In order to sort them, we don't need to know or access anything else about any of the classes
Thus, if they all implement an interface that defines the comparison, we can sort them all with a single method that is defined in terms of that interface
Huh? Qu?
Perhaps it will make more sense if we develop an examplebut first we will need some background!
Simple Sorting
What does it mean to sort our data?
Consider an array, A of N items:
A[0], A[1], A[2], , A[N-1]
Simple Sorting
How do we sort?
There are MANY ways of sorting data
Sorting has been widely studied in computer science
SelectionSort
SelectionSort is very intuitive:
Idea:
Find the smallest item and swap it into index 0 Find the next smallest item and swap it into index 1 Find the next smallest item and swap it into index 2 Find the next smallest item and swap it into index N-2 What about index N-1?
35
50
20
40
75
10
15
60
SelectionSort
Lets look at the code
SortInt.java and ex25.java Note:
Done in a modular way utilizing methods Trace it on the example from previous slide Done here in terms of only one type int
Comparable Interface
Consider the Comparable interface:
It contains one method: int compareTo(Object r); Returns a negative number if the current object is less than r, 0 if the current object equals r and a positive number if the current object is greater than r Look at Comparable in the API Not has restrictive as equals() can throw ClassCastException
Thus, we can sort Comparable data without knowing anything else about it
Awesome! Polymorphism allows this to work
Using Comparable
Think of the objects we want to sort as black boxes
We know we can compare them because they implement Comparable We dont know (or need to know) anything else about them
Thus, a single sort method will work for an array of any Comparable class
Lets write it now, altering the code we already know from our simple sort method See Sorting.java and ex26.java
Also see SortingT.java and ex26T.java
Binary Search
Consider Sequential Search again
See Procedural Programming slides and ex8.java
Note that in the worst case we look at every item in the array
We say this is a linear run-time or time proportional to N, the number of items in the array
Can we do better?
If the data is unsorted, no
It could be any item, so in the worst case well have to try them all
Binary Search
Idea of Binary Search:
Searching for a given key, K Guess middle item, A[mid] in array
If A[mid] == K, we found it and are done If A[mid] < K then K must be on right side of the array If A[mid] > K then K must be on left side of the array
Either way, we eliminate ~1/2 of the remaining items with one guess Search for 40 below
10
15
20
35
40
50
60
75
Binary Search
What if item is not in array? We need a stopping condition in the not found case
Binary Search
So is Binary Search really an improvement over Sequential Search
Each guess removes ~ of the remaining items Thus the total number of guesses cannot exceed the number of times we can cut the array in half until we reach 0 items
Ex: 32 16 8 4 2 1 => 6 Generally speaking, for N items in the array, in the worst case we will do ~log2N guesses This is MUCH better than Sequential Search, which has ~N guesses in the worst case You will discuss this more in CS 0445 and CS 1501
Collections
Sorting and Searching are used often With Generics, the code only needs written once
Can then be used in any situation Provided were dealing with Comparable objects
Collections
Java refers to these as Collections
Containers of other data objects Collect related objects into a single object
Separate Interface with which we access the stored objects from the Implementation
Used often enough that Java provides standard implementation of each type of Collection
Collection
List (Vector) Set
SortedSet
Queue
Map (Hashtable)
SortedMap
Stack
List
What is a List?
Lets consider lists we make in every day life What kinds are there? What information does each store? How can we access and change each? A container of them Implied arbitrary order of the elements Size shrinks and grows with the number of items in the list We can access an element if we know its position in the List We can insert an item to any position We can remove an item from any position
List Implementation
We now have a concept of a List
A scheme by which we store and access elements in the List This is defined by the List Interface in Java
See Java API Notice add(), get(), remove(), contains() Assumes objects have well defined equals() method
LinkedList
Uses a chain of nodes, each of which stores a single item
ArrayList
We use an array to store the items in the List
Arrays have a fixed size List has an arbitrary size If our list has n elements, we need an array of size n or MORE
We can leave extra empty spaces to store elements added later We can resize the array if we need more space
But consider
Inserting or adding an element
Need free the index where the element is to be stored Need to keep maintain the same order with the new element added Requires shifting some elements to higher indices
Removing an element
Array now has an unused index Have to shift elements to lower indices to keep ordering
A lot of shifting!!!
NOTE: The Vector class provides the same implementation, but provides synchronization for multithreaded applications (slower)
LinkedList
The strict array indexing causes this need for shifting
Can we avoid this?
If we keep a reference to the first Node, we can access any of them by repeatedly accessing the next reference
Show on board Getting element at position I requires us to traverse the chain
Disadvantage over an array
LinkedList when
Frequent additions and removals at arbitrary positions, especially beginning
This is discussed further in CS 0445 We want to hide the implementation of a collection as much as possible
We only care that we have List List Interface provides this abstraction See ex27.java
Set
Similar to a List except
No order to the elements Cannot contain duplicate objects
Operations on a Set
Add an element add() Remove an element remove() Test for inclusion contains() Union (combine elements from two Sets) addAll() Intersection (keep elements two Sets have in common) retainAll() Difference (keep elements not found in a second Set) removeAll()
Implementations
HashSet stores elements in a Hashtable LinkedHashSet: similar to HashSet TreeSet stores elements in a Binary Tree
HashSet
Imagine implementing a Set with an array
To maintain the constraint that the Set doesnt contain duplicates, wed have to do something like: for(int i = 0; i < count; i++) if(array[i].equals(newElement)) return; //dont add new element array[count++] = newElement; We have to check each item before knowing if a duplicate existed in the array
What if we could know the index where a duplicate would be stored if it was in the Set?
Just check the element(s) at that index with equals() Add the new Element if not there
Iterators
Weve seen two types of Collections so far
List Set
Recall how often weve used the following code with arrays
for(int i = 0; i < array.length; i++) {} Loop for each element of the array Use variable i to keep track of position in the array
An Iterator is a object that can traverse over and provide access to each element
User doesnt know how it finds the next element
Using Iterators
Iterator class defines two methods
hasNext()
returns true if there is another element to examine
next()
returns the current element as an Object Prepares the iterator to return the next element
See ex28.java
Order in Sets
The HashSet Class is the simplest implementation of the Set interface
Iterator can return the elements in any particular order Can be chaotic
It can be advantageous to ensure an Iterator returns the elements is some consistent order Two implementations of the Set Interface do this
LinkedHashSet
Like HashSet Iterator returns elements in the order in which they were added to the Set
TreeSet
Iterator returns elements in sorted order Requires stored objects to be Comparable Elements actually stored in sorted order in a binary tree
See ex28b.java
Queue
We often dont need to be able to modify all a Collections elements
Can even be advantageous to prevent access to every element Force modification of the Collection according to specific rules
Implementation
How could we implement a Queue? An array could impose the needed ordering
Would require a lot of shifting Circular array is still cumbersome
See ex28c.java
Stack
Consider a stack of plates on a spring in a bin in a cafeteria
When a plate is added, spring compresses, hiding all plates below Only plate that can be removed is the top plate
The last one that was added
A Stack is a data structure where objects can only be added to or removed from the top
Last item added is the first to be removed LIFO ordering Last In, First Out
Stack Implementation
How would this best be implemented?
Array Linked list
Either would be efficient Array doesnt require the extra storage of saving a reference to each object java.util.Stack is a concrete class
Can create instances of it Ex: Collection collection = new Stack(); Stack stack = new Stack();
See ex28d.java
Map
The Map Interface differs from a Collection Defines a mapping from one set of objects to another
Like a function in Mathematics: y = f(x) Given an object, x, the map returns an object, y Refer to x as the key
A Map allows us to impose a logical relationship between an object and its index (key)
Ex:
The title a CD could be the index of our AbstractCD class A Persons name could index their phone number (Phone Book)
Map Implementation
Recall our discussion of a Set
Used hashCode() to store object in array Used compareTo() to order object in tree
See ex29.java
The keySet() method returns a Set containing all the keys stored in the Map
Map cannot contain duplicate keys Iterate over this Set
The values() method returns a Collection of all objects that have an associated key
May contain duplicate objects Iterate over this collection
The entrySet() method returns a Set of all the (key, value) pairs
Each object in the Set is of the type Map.Entry Iterate over this Set
Generics
From the previous slides
Each collection or Iterator returns an Object Necessary since it is designed to work for any kind of object Requires us to cast the reference to an instance that we need Ex: List list = new LinkedList(); list.add(Some Pig); String s = (String) list.get(0); String t = (String) list.iterator().next();
The cast can be annoy The list may also not really contain Strings Wed like to force the List to only contain specific types
We wouldnt need the cast We could be sure what type of objects the List contained
Generics
We parameterize the instance of our List with the type of object we expect it to contain using the <> syntax
Ex List<String> list = new LinkedList<String>(); list.add(Some Pig); String s = list.get(0); String t = list.iterator().next(); Declares a List of Strings instead of a simple List Compiler can now ensure only Strings are added to this particular list We no longer need the casts
The <E> declares that a type must be used when an instance is created
The type is then used in place of anywhere the E is used in the class definition
e.g. add(E x);
Wildcards in Generics
Ex: In ex28, we had the method:
public void printCollection(Collection c) { for(Object o : c) System.out.println(o); }
With the subtype restriction, we cant pass anything other than List<Object>
Not very helpful We can handle any type of List, though
Wildcards in Generics
Ex:
public void printCollection(Collection<?> c){ for(Object o : c) System.out.println(o); }
See ex30.java
Bounded Wildcards
Recall our Animal, Fish, Bird, Person classes Wed like to write a method like:
public void printAnimals(Collection<Animal> c) { for(Animal a : c) { a.characteristics(); a.move(); } }
This can then only accept a Collection of Animal If we use a wildcard (?), we lose access to the move() and characteristics() methods
Bounded Wildcards
The solution is a bounded wildcard:
printAnimals(Collection<? extends Animal> c) { for(Animal a : c) { a.characteristics(); a.move(); } }
Bounded Wildcards
The ? extends MyClass syntax defines an upper bound Likewise, ? super MyClass can define a lower bound
Means any class that is a superclass of class T E.g. Comparable<? super T>
Dealing with a comparable object that can compare itself with any superclass of T
Generic Methods
Suppose we want to write a method copies an array into a Collection: public void fromAtoC(Object[] a, Collection<?> c) { for(Object o : a) c.add(o);} Weve already learned that we cant do this with the wildcard We can use generic methods public <T> void fromAtoC(T[] a, Collection<T> c) { for(T o : a) c.add(o);} All the same wildcard rules apply public <T> listCopy(List<? extends T> source, List<T> dest){} When to use:
Notice the dependency between the types in the parameters If the dependency does not exist, you should use wildcards instead Also used if the return type of the method is type dependent