Introduction to Software Engineering

Download Report

Transcript Introduction to Software Engineering

CE203 - Application Programming
Part 3
Autumn 2014
CE203 Part 3
1
Inheritance 1
Suppose we have written a class Person to store information
about persons (such as name and date of birth) and wish to
write a class to store information about students. A student is
a person (with some extra attributes such as a registration
number) so we should implement the class Student as a
subclass of Person. We can then apply methods from the
Person class to objects of type Student and also store
objects of type Student in arrays or other collections of
persons.
Autumn 2014
CE203 Part 3
2
Inheritance 2
public class Student extends Person
{ private int regno;
public Student(String s, Date d,
int r)
{ super(s,d);
regno = r;
}
public int regNumber()
{ return regno;
}
public String toString()
{ return super.toString()+" Regno: "+regno;
}
}
Autumn 2014
CE203 Part 3
3
Inheritance 3
To invoke the constructor from the superclass within the
subclass constructor we have used the super statement,
which, if used, must be the first statement in the constructor.
If there is no such statement the no-argument constructor of
the superclass will be invoked implicitly; if there is no such
constructor the compiler will report an error.
The keyword super has also been used to prefix the call of
toString to ensure that the Person version is called; if this
prefix was not used dynamic binding would result in a
recursive call to the Student version.
Autumn 2014
CE203 Part 3
4
Inheritance 4
Since all students are persons a variable of type Person may
refer to an object of type Student:
Person harry = new Student(”Harry Potter”,
new Date(23,1,1981), 9907077);
If we were to apply toString to the variable harry the
student version would be called.
It is not legal to write
int r = harry.regNumber();
since, although we know that harry refers to a student, the
variable is of type Person and the Person class has no
regNumber method.
Autumn 2014
CE203 Part 3
5
Inheritance 5
In order to apply the regNumber method to a student
referenced by a variable of type person we must perform a
downcast, telling the compiler that the variable will refer to a
subclass object.
int r = ((Student)harry).regNumber();
Note that the highlighted parentheses are necessary since
method application has a higher precedence than
downcasting; without the parentheses the downcast would be
applied to the result of the regNumber call.
If the variable harry did not refer to a student an exception
of type ClassCastException would be thrown when the
code was run.
Autumn 2014
CE203 Part 3
6
Inheritance 6
Since a student is a person we may of course store students in
an array of objects of type Person.
If we have such an array and wish to process all of the
students in the array (but not non-students) we need to
determine whether a particular element is a student. To do
this we must use the instanceof operator, which tests
whether a superclass object is a member of a particular
subclass.
Autumn 2014
CE203 Part 3
7
Inheritance 7
To print details of all students in an array a of objects of type
Person we could use:
for (int i = 0; i<a.length; i++)
if (a[i] instanceof Student)
System.out.println(a[i]);
Similarly, to print details of all non-students in the array we
could use:
for (int i = 0; i<a.length; i++)
if (!(a[i] instanceof Student))
System.out.println(a[i]);
Again the highlighted parentheses are essential.
Autumn 2014
CE203 Part 3
8
Inheritance 8
Since a student is a person an array of students is an array of
persons and we may use an array of students anywhere an
array of persons is expected. Hence if Employee is a subclass
of Person, but not of Student, and s has been declared as an
array of students, the following code fragment is legal:
static void changeLast(Person[] p)
{ p[p.length-1] = new Employee(……);
}
……
changeLast(s);
……
Autumn 2014
CE203 Part 3
9
Inheritance 9
Although the code on the previous slide will compile without
any problems it will not run successfully since we cannot
store an employee in an array of students. When we try to run
the code an exception of type ArrayStoreException will be
thrown.
Autumn 2014
CE203 Part 3
10
Inheritance 10
If a method has an argument of type Object[] , any array of
objects may be passed to it (but not an array of primitive
values).
We can use this to write a method which will copy the
contents of one array into another:
static void arrCopy(Object[] source,
Object[] dest)
{ if (dest.length<source.length)
throw new ArrayStoreException();
int i;
for (i = 0; i<source.length; i++)
dest[i] = source[i];
}
Autumn 2014
CE203 Part 3
11
Inheritance 11
The method on the previous slide will work if the elementtypes of the two arrays passed as arguments are the same and
also if the source array type is a subclass of the destination
array type.
It will also work in certain other circumstances, as long as all
of the elements of the source array are of the correct type for
the destination. For example, we can copy an array of type
Person into an array of type Student if all of the people in
the array happen to be students, but an exception will be
thrown if any of the elements is not a student.
Autumn 2014
CE203 Part 3
12
Abstract Classes 1
Suppose that we wish to write a program to draw and
manipulate simple shapes (circles, squares, etc.) on a frame.
We might write a square class to store information about
squares.
public class Square
{ private Color col;
private int xpos, ypos; // centre coords
private int slen;
// side length
public Square(Color c, int x, int y, int s)
{ col = c; xpos = x; ypos = y; slen = s;
}
Autumn 2014
CE203 Part 3
13
Abstract Classes 2
public void setColor(Color c)
{ col = c;
}
public void move(int xdist, int ydist)
{ xpos += xdist; ypos += ydist;
}
public void draw(Graphics g)
{ g.setColor(col);
g.drawRect(xpos-slen/2, ypos-slen/2,
slen, slen);
}
}
Autumn 2014
CE203 Part 3
14
Abstract Classes 3
Assume that we also have Circle and Triangle classes with
setColor, move and draw methods and that details of all of
the shapes currently displayed are stored in an array called
shapes whose item-type is Object.
Suppose we wish to change the colour of all of the shapes in
the array to blue. We would like to write
for (int i = 0; i<shapes.length; i++)
shapes[i].setColor(Color.blue);
but cannot do this since the Object class has no method
called setColor.
Autumn 2014
CE203 Part 3
15
Abstract Classes 4
The solution is to introduce a new class called Shape with a
setColor method and declare all of the shape classes as
subclasses of this class. We can then use an array of Shape
objects instead of an array of items of type Object.
The new class should be declared as an abstract class – this
means that all of its members must be instances of one of its
subclasses and explicit calls to new Shape(……) are not
allowed.
We should place the attributes and methods that are common
to all shapes in the Shape class, as seen on the next slide.
Autumn 2014
CE203 Part 3
16
Abstract Classes 5
public abstract class Shape
{ protected Color col;
protected int xpos, ypos;
public Shape(Color c, int x, int y)
{ col = c; xpos = x; ypos = y;
}
public void setColor(Color c)
{ col = c;
}
public void move(int xdist, int ydist)
{ xpos += xdist; ypos += ydist;
}
}
Autumn 2014
CE203 Part 3
17
Abstract Classes 6
The instance variables have been declared as protected so
that the subclasses can access them in their draw methods.
Since the Square class will now inherit attributes and
methods from Shape it will be much more concise.
public class Square extends Shape
{ private int slen; // side length
public Square(Color c, int x, int y, int s)
{ super(c, x, y); slen = s;
}
public void draw(Graphics g) // as before
}
Autumn 2014
CE203 Part 3
18
Abstract Classes 7
Since the draw methods for each individual shape are
different they must be written in the individual classes.
However, to allow a loop such as
for (int i = 0; i<shapes.length; i++)
shapes[i].draw(g);
to be written, the Shape class must have a draw method.
The use of dynamic binding means that this method will
never be called so we could simply write a method that does
nothing. However, the preferred approach is to declare the
method as abstract.
Autumn 2014
CE203 Part 3
19
Abstract Classes 8
public abstract class Shape
{ protected Color col;
protected int xpos, ypos;
public Shape(Color c, int x, int y)
{ col = c; xpos = x; ypos = y;
}
// setColor and move methods as before
public abstract void draw(Graphics g);
}
Note that only abstract classes can have abstract methods.
Autumn 2014
CE203 Part 3
20
Interfaces 1
An interface is essentially an extreme example of an abstract
class – all of its methods are abstract. There are, however,
some significant differences between interfaces and abstract
classes:
• the only variables that an interface can have are public
static final ones (i.e. constants)
• an interface can not have any constructors
• a class does not inherit from an interface but instead is
said to implement it and uses the keyword implements
instead of extends
Autumn 2014
CE203 Part 3
21
Interfaces 2
The most common use of interfaces is to provide a
specification of the methods of a class that is to be used in
one package but implemented in different ways in many
others – ActionListener is a typical example; classes that
implement it are written by the programmer but used by
classes in the java.awt.event package.
A class may implement more than one interface:
class Silly implements ActionListener,
Collection, Comparable
Autumn 2014
CE203 Part 3
22
Comparing Objects 1
Suppose we have a Date class such as the one seen
previously and wish to compare some dates that have been
input with a specific date. If we try to use code such as
Date xmasDay = new Date(25,12,2014);
Date theDay = Date.getDate();
if (theDay==xmasDay)
……
we will find that the result of the comparison is always false.
When comparing references to two objects they are regarded
as equal only if they refer to the same object.
Autumn 2014
CE203 Part 3
23
Comparing Objects 2
To determine if two objects have the same contents we should
use an equals method that compares the contents, overriding
the equals method inherited from the Object class:
if (theDay.equals(xmasDay))
……
To override the inherited version an equals method must
have an argument of type Object and downcast its argument.
An equals method for the Date class is provided on the next
slide.
Autumn 2014
CE203 Part 3
24
Comparing Objects 3
public boolean equals(Object o)
{ // check if the argument is a non-null date
if (o!=null && o instanceof Date)
{ Date arg = (Date)o;
return y==arg.y && m==arg.m && d==arg.d;
}
else
// if the argument isn’t a date they cannot
//
be equal
return false;
}
Autumn 2014
CE203 Part 3
25
The Java Collections Framework 1
The Java Collections framework provides a number of
classes that can be used to store various kinds of collections
of objects. These kinds are distinguished by properties such
as whether duplicate elements are allowed and whether the
elements have a defined order.
Methods that are common to all collection classes are
declared in an interface called Collection from the package
java.util.
Autumn 2014
CE203 Part 3
26
The Java Collections Framework 2
public interface Collection
{ public boolean isEmpty();
public int size();
public boolean contains(Object o);
public boolean containsAll(Collection c);
public boolean add(Object o);
public boolean addAll(Collection c);
public void clear();
Autumn 2014
CE203 Part 3
27
The Java Collections Framework 3
public
public
public
public
public
public
public
public
boolean remove(Object o);
boolean removeAll(Collection c);
boolean retainAll(Collection c);
Object[] toArray();
Object[] toArray(Object[] a);
Iterator iterator();
int hashCode();
boolean equals(Object o);
}
Autumn 2014
CE203 Part 3
28
The Java Collections Framework 4
The contains and containsAll methods use equals to
compare elements; the latter returns true if the collection
contains at least one instance of each of the elements of the
argument.
The add method may fail if an attempt is made to add an
element that is already present to a collection that does not
allow duplicates; hence a result is returned to indicate whether
the addition was successful.
For the same reasons the addAll method may add to the
collection some, all or none of the elements in its argument; it
will return true if at least one element has been added.
Autumn 2014
CE203 Part 3
29
The Java Collections Framework 5
The remove method attempts to remove one instance of its
argument from the collection; if the argument occurs more
than once, other instances will be retained. The result
indicates whether an instance was found and removed.
The removeAll method will remove all instances of all of the
elements contained in the argument; true will be returned if
at least one element has been removed.
The retainAll method will remove all elements that are not
contained in the argument; true will be returned if at least
one element has been removed.
Autumn 2014
CE203 Part 3
30
The Java Collections Framework 6
The toArray methods return an array containing all of the
elements of the collection; duplicate elements in the collection
will occur more than once in the array, so the number of items
in the array will be equal to the size of the collection.
The version without an argument will always return a new
array of type Object[]. The other version allows existing
arrays to be reused by attempting to store the elements of the
collection in the array supplied as an argument. If this array is
too small a new array of the same type as the argument will be
created. An exception of type ArrayStoreException may be
thrown if any of the elements cannot be stored in the array
due to a type mismatch.
Autumn 2014
CE203 Part 3
31
The Java Collections Framework 7
The behaviour of the equals method depends on the kind of
collections being compared – two sets are equal if they
contain the same elements but two lists are equal only if they
contain the same elements in the same order. The result will
always be false if an attempt is made to compare two
collection objects of different types, even if they contain the
same elements.
[ The hashCode and iterator methods will be explained
later. ]
Autumn 2014
CE203 Part 3
32
The Java Collections Framework 8
A simple program fragment demonstrating the use of methods
from the Collection interface is presented on the next slide.
We have to select a particular class that implements the
interface.
Note that the elements of collections are objects – when we
use the int variable i as an argument to a method from the
Collection interface a new Integer object containing the
value i is implicitly created.
If we were to modify this program fragment to use, for
example, Vector instead of TreeSet the behaviour would be
different since vectors may contain duplicate elements
whereas sets may not.
Autumn 2014
CE203 Part 3
33
The Java Collections Framework 9
Collection col = new TreeSet();
col.add(4);
int i;
for (i = 0; i<6; i++)
if (col.add(i))
System.out.println("added "+i);
else
System.out.println("could not add "+i);
for (i = 2; i<5; i++)
col.remove(i);
for (i = 0; i<7; i++)
if (col.contains(i))
System.out.println("found "+i);
else
System.out.println("could not find "+i);
Autumn 2014
CE203 Part 3
34
The Java Collections Framework 10
We now provide a method that will remove from a collection
of objects of type String all strings whose length is greater
than 10. If the collection contains any non-strings an
exception will be thrown since downcasting will fail.
static void removeLongStrings(Collection c)
{ Object[] arr = c.toArray();
for (int i = 0; i<arr.length; i++)
{ String s = (String)arr[i];
if (s.length()>10)
c.remove(s);
}
}
Autumn 2014
CE203 Part 3
35
The Java Collections Framework 11
Note that if the collection contained two instances of the same
long string this string would appear twice in the array and
hence both instances would be removed.
The method is rather inefficient; having found a long string in
the array, the remove method when called will have to search
for it again in the collection. We could implement a much
more efficient version by moving through the collection an
item at a time, removing long strings as we find them. We
shall later see how to do this using an iterator.
Autumn 2014
CE203 Part 3
36
Vectors 1
The Vector class provides objects similar to arrays but whose
size can change. This class was provided in versions of the
Java library that pre-dated the introduction of the Collection
interface, but extra methods were added to it in subsequent
versions to enable it to implement the interface.
A vector has both a size and a capacity – the size refers to the
number of elements currently held in the vector whereas the
capacity refers to the amount of memory space currently
allocated. The initial capacity may be supplied as an
argument to a constructor when the vector is created.
Autumn 2014
CE203 Part 3
37
Vectors 2
At any time the size of a vector must be less than or equal to
the capacity; if the addition of an element would cause the
size to become larger than the capacity the latter will be
increased.
To optimise use of space the capacity should be increased by a
small amount, but to optimise performance a larger increase
would be preferred, reducing the number of times the capacity
has to change. To allow the programmer to select a trade-off
between these two conflicting criteria, the amount by which
the capacity should be increased can be specified in a
constructor; if this has not been done the size will be doubled.
Autumn 2014
CE203 Part 3
38
Vectors 3
The capacity of a vector is never decreased automatically
when items are removed, but there is a method called
trimToSize that will reduce the capacity to the current size.
This would normally be used only if no more items are going
to be added to the vector or the capacity has become much
larger than the size.
In addition to the size method specified in the Collection
interface the Vector class has a method called capacity that
returns the current capacity.
Autumn 2014
CE203 Part 3
39
Vectors 4
The Vector class has four constructors. The first has no
arguments and sets the capacity to 10.
The second constructor takes an argument of type int and
uses that value for the initial capacity.
The third takes two arguments of type int, the first specifying
the initial capacity and then second the amount by which the
capacity should be increased when necessary.
All of these initialise the vector to be empty.
The fourth constructor takes an argument of type Collection
and copies the contents of the collection into the vector.
Autumn 2014
CE203 Part 3
40
Vectors 5
In order to permit a vector to be used like an array, methods
are provided to examine and replace the contents of a
particular location. The elementAt method is used to
examine the contents (indexing starts from 0 as with arrays).
An ArrayIndexOutOfBoundsException will be thrown if
the argument is out of range.
There are also methods called firstElement and
lastElement. These will throw an exception of type
NoSuchElementException if the vector is empty.
These exception types are subclasses of RunTimeException.
Autumn 2014
CE203 Part 3
41
Vectors 6
To replace the contents of a specific location in a vector the
setElementAt method can be used.
This takes two
arguments, a reference to an object and the index of the
location in which it is to be stored.
There is also a method called insertElementAt that shifts
elements to make room for a new one and a method called
removeElementAt which will also shift the elements.
These methods may throw an exception of type
ArrayIndexOutOfBoundsException.
Autumn 2014
CE203 Part 3
42
Vectors 7
The add method as specified in the Collection interface
will append an element to the end of the vector and will
always return true.
The remove method will remove the first instance of the
element if there is more than one occurrence.
Autumn 2014
CE203 Part 3
43
Vectors 8
In addition to the contains method specified in the
Collection interface there is a method that will find the
location of an item if it is present in the vector. This method,
called indexOf, will return the location of the first
occurrence of its argument, or the value -1 if the argument is
not found in the vector.
There is also a lastIndexOf method, and two-argument
versions of both methods that start searching from a
particular location (specified by a second argument) instead
of from the beginning or end of the vector.
Autumn 2014
CE203 Part 3
44
Vectors 9
Here is a method to determine how many times an item
occurs in a vector.
public static int occurrences(Vector v,
Object o)
{ int count = 0;
int pos = v.indexOf(o);
while (pos!=-1)
{ count++;
pos = v.indexOf(o, pos+1);
}
return count;
}
Autumn 2014
CE203 Part 3
45
Vectors 10
How does the Vector class differ from ArrayList?
In what context would you use Vector and when would you
use ArrayList?
Autumn 2014
CE203 Part 3
46
Generics / Parameterised Types
As a consequence of the introduction of generics in Java 5.0
collection classes and interfaces may now be parameterised
by types, e.g. Collection<Integer>.
If a type parameter is provided the argument and result types
of the methods will be more specific than Object, so, for
example, for an object of type Vector<Date> the add
method would expect an argument of type Date and the
elementAt method would return a Date object.
Autumn 2014
CE203 Part 3
47
The Set Interface
The Set interface extends the Collection interface, but
adds no extra definitions:
public interface Set extends Collection {}
Classes that implement this interface must therefore provide
all of the methods specified in the Collection interface and
should exhibit the expected properties of sets (e.g. no
duplicate elements are allowed, two sets with the same
elements are equal).
The collections framework provides two classes that
implement the interface, HashSet and TreeSet.
Autumn 2014
CE203 Part 3
48
Iterators 1
Iterators can be used to access the contents of a collection
one-by-one.
An iterator may be thought of as a sequence of elements,
together with a place-marker that lies between adjacent
elements in this sequence. The sequence comprises all of the
elements in the collection; for a hash set the order in which
the elements occur in the sequence is unspecified, for a list or
vector the elements will appear in the same order as in the
collection.
Autumn 2014
CE203 Part 3
49
Iterators 2
The iterator method specified in the Collection interface
will return an iterator for the collection to which it is applied;
the place-marker will be in front of the first element of the
sequence.
The iterator will implement the Iterator interface, which
specifies three methods:
public interface Iterator
{ public boolean hasNext();
public Object next();
public void remove();
}
Autumn 2014
CE203 Part 3
50
Iterators 3
The hasNext method can be used to determine whether there
are any more elements in the sequence after the place-marker;
a loop to process the contents of a collection will generally
take the form
while (it.hasNext())
// do something with it.next()
The next method returns a reference to the next item in the
sequence and also advances the place-marker past that item;
an exception of type NoSuchElementException will be
thrown if the place-marker is at the end of the sequence.
Autumn 2014
CE203 Part 3
51
Iterators 4
We could use the following method to print all of the items in
a collection, one item per line:
public static void print(Collection c)
{ Iterator it = c.iterator();
while (it.hasNext())
System.out.println(it.next());
}
Autumn 2014
CE203 Part 3
52
Iterators 5
The following method will print all of the strings in a
collection, but not any other items:
public static void printStrings(Collection c)
{ Iterator it = c.iterator();
while (it.hasNext())
{ Object o = it.next();
if (o instanceof String)
System.out.println(o);
}
}
Note that in order to visit all of the elements we must ensure
that next is called only once in the loop body.
Autumn 2014
CE203 Part 3
53
Iterators 6
If a collection is parameterised by a type its iterator will also
be parameterised by that type and the next method will
return an object of that type. The following method will
calculate and return the total length of the items in a
collection of strings.
public static int totalLength(
Collection<String> c)
{ Iterator<String> it = c.iterator();
int tot = 0;
while (it.hasNext())
tot += it.next().length();
return tot;
}
Autumn 2014
CE203 Part 3
54
Iterators 7
An iterator can be used only for a single traversal of the
sequence of elements so if we wish to traverse them again it
is necessary to obtain a new iterator.
The method on the next slide will print all the even integers
in a collection of integers and subsequently print all of the
odd integers. Note that we apply the intValue method to an
Integer object when we need to retrieve the value stored in
the object as an int. Also recall that the % operator is the
modulo or remainder operator – a%b gives the remainder that
would be obtained when dividing a by b.
Autumn 2014
CE203 Part 3
55
Iterators 8
public static void printEvenThenOdd(
Collection<Integer> c)
{ Iterator<Integer> it = c.iterator();
while (it.hasNext())
{ Integer i = it.next();
if (i.intValue()%2==0)
System.out.println(i); }
it = c.iterator();
while (it.hasNext())
{ Integer i = it.next();
if (i.intValue()%2!=0)
System.out.println(i); }
}
Autumn 2014
CE203 Part 3
56
Iterators 9
If the objects in a collection have methods that allow them to
be modified such methods can be applied. Assuming that a
class called Employee has methods salary and setSalary
we could apply the following method to increase the salary of
each member of a collection of employees.
public static void increaseSalary(
Collection<Employee> c)
{ Iterator<Employee> it = c.iterator();
while (it.hasNext())
{ Employee e = it.next();
e.setSalary(e.salary()+500);
}
}
Autumn 2014
CE203 Part 3
57
Iterators 10
Methods that change the contents of an object in a set should
be used with great care – they can have unpredictable effects
if used inappropriately. For example a change to an element
could result in duplicate elements. Additionally a change to
an element in a hash set could change the hash value of that
element, in which case subsequent searches might behave
incorrectly.
These problems do not occur with vectors and other types of
list so methods that change the contents of objects in these
types of collection may be freely used.
Autumn 2014
CE203 Part 3
58
Iterators 11
In Java 5.0 a new form of for loop (known as the enhanced
for statement) has been introduced, which allows the
programmer to iterate through the contents of a collection
without having to explicitly declare an iterator. The statement
for (T x:c) ……
serves as a shorthand notation for
Iterator<T> it = c.iterator();
while (it.hasNext())
{ T x = it.next();
……
}
Autumn 2014
CE203 Part 3
59
Iterators 12
We can rewrite the increaseSalary method using an
enhanced for statement:
public static void increaseSalary(
Collection<Employee> c)
{ for (Employee e:c)
e.setSalary(e.salary()+500);
}
The introduction of the enhanced for statement does not
eliminate the need to be able to understand and use iterators;
if we wish to use the remove method from the Iterator
interface we must explicitly obtain an iterator for the
collection.
Autumn 2014
CE203 Part 3
60
Iterators 13
The remove method from the Iterator interface can be
used to remove items from a collection. When applied to the
iterator it will remove from the collection the object returned
by the most recent call to next. This method will throw an
IllegalStateException if next has not been called, and
also if remove is called twice without a call to next between
the two calls.
We could use the method on the following slide to remove all
non-students from a collection of objects of type Person.
(We assume, as in earlier examples, that Student is a
subclass of Person.)
Autumn 2014
CE203 Part 3
61
Iterators 14
public static void removeNonStudents(
Collection<Person> c)
{ Iterator<Person> it = c.iterator();
while (it.hasNext())
if (!(it.next() instanceof Student))
it.remove();
}
Autumn 2014
CE203 Part 3
62