losn 3

4
3
Interface, exceptions, generik, iteratorer
Interface, exceptions, generik, iteratorer
U 18. Ett interface innehåller bara metodrubriker utan implementeringar (publika, abstrakta
metoder). Man får också ha konstanter (attribut deklarerade static final). Ett interface
utgör ett kontrakt mellan klasser som implementerar det och dess användare. En klass
som implementerar ett interface måste implementera alla de abstrakta metoderna i interfacet.
U 19. Både abstrakta klasser och interface innehåller (oftast) abstrakta metoder. Man kan inte
skapa objekt av interface eller av abstrakta klasser.
Ett interface innehåller bara metodrubriker, dvs. abstrakta metoder och ev. konstanter.
En abstrakt klass kan (precis som en ”vanlig” klass) innehålla attribut, konstruktorer och
andra metoder.
U 20.
a) Lägg till implements Resizable i klassrubriken och implementera metoden downSize.
public class Rectangle implements Resizable {
private double height;
private double width;
public Rectangle(double h, double w) {
height = h;
width = w;
}
public void downSize(int scaleFactor) {
height = height / scaleFactor;
width = width / scaleFactor;
}
}
...
b) 1 och 3 är korrekta. Alternativ 2 är fel eftersom man inte kan skapa objekt av interface.
U 21.
a) /**
* Scales down the object with scaleFactor.
* @param scaleFactor the scale factor which is used to reduce the
* size of the object
* @throws IllegalArgumentException if the specified scale factor is <= 0
*/
public void downSize(int scaleFactor) {
if (scaleFactor <= 0) {
throw new IllegalArgumentException("The scale factor has the wrong value: "
+ scaleFactor);
}
height = height / scaleFactor;
width = width / scaleFactor;
}
b) try {
r.downSize(n);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
Interface, exceptions, generik, iteratorer
5
U 22. /** Returns true if this collection contains the specified element. */
public boolean contains(Object x) {
for (int i = 0; i < size; i++) {
if (theCollection[i].equals(x)) {
return true;
}
}
return false;
}
/* Creates an new array that is twice the size of the current array and
copies the content of the current array into the new one. */
private void doubleArray() {
E[] tmp = (E[]) new Object[2 * theCollection.length];
for (int i = 0; i < size; i++) {
tmp[i] = theCollection[i];
}
theCollection = tmp;
}
Alternativ implementering av doubleArray:
private void doubleArray() {
E[] tmp = (E[]) new Object[2 * theCollection.length];
System.arraycopy(theCollection, 0, tmp, 0, size);
theCollection = tmp;
}
U 23. Inuti metodencontains anropas metodn equals för att jämföra två objekt.
I contains har den formella parametern x den deklarerade typ Object. För anropet
theCollection[i].equals(x) kommer därför kompilatorn att bestämma signaturen för
metoden att vara equals(Object x).
När vi exekverar metoden refererar theCollection[i] till ett objekt av typen Person.
Då börjar sökandet efter en metod med korrekt signatur i denna klass. Det finns ingen
sådan. Då fortsätter letandet i superklassen, som i detta fall är Object. Där finns en metod
med rätt signatur, som exekveras. Metoden equals i Object är emellertid implementerad
så att den returnerar true om och endast om de båda jämförda objekten är identiska,
vilket inte är fallet här.
Problemet är alltså att metoden equals i klassen Person överlagrar metoden equals i
klassen Object men inte skuggar den. För att hitta Lisa skall vi i stället skugga metoden
equals(Object) i klassen Person. Det gör vi genom att ge den samma signatur som
equals i klassen Object:
public class Person {
String name;
...
}
public boolean equals(Object p) {
return name.equals(((Person) p).name);
}
...
Kompilatorn kommer att göra samma beslut som beskrivits ovan. Skillnaden är nu att
under exekvering hittas en metod med rätt signatur i klassen Person och det blir därför
denna som exekveras.
6
Interface, exceptions, generik, iteratorer
Anm. Det ”kontrakt” som finns för metoden equals i Java innebär bl. a:
• Om x inte är null ska x.equals(null) returnera false.
• Om x och y är av olika klasser ska x.equals(y) returnera false.
Metoden equals i klassen Person kan därför se ut så här:
public boolean equals(Object p) {
if (p instanceof Person) {
return name.equals(((Person) p).name);
} else {
return false;
}
}
Uttrycket p instanceof Person returnerar true om p:s typ är Person eller någon subklass till Person. Uttrycket returnerar false om p har värdet null.
Lösningen tillåter alltså att subklasser ärver equals-metoden. Man kan använda equals
i en arvshieariki och jämföra ”subklassobjekt” och ”superklassobjekt”. Denna lösning kan
dock leda till fall där equals inte uppfyller kraven i specifikationen. Detta undviks om
man bara tillåter jämförelser mellan objekt av samma typ:
public boolean equals(Object p) {
if (p == this) {
return true;
}
if (p == null) {
return false;
}
if (this.getClass() != p.getClass()) {
return false;
}
return name.equals(((Person) p).name);
}
U 24. Interfacet Iterable<E> har en enda metod: Iterator<E> iterator(). Klasser som implementerar detta interface måste alltså ha en operation som returnerar ett objekt av en klass
som implementerar interfacet Iterator<E>. Klasser som implementerar Iterable<E> representerar olika slags samlingar av element och operationen iterator() ger användare
av klassen tillgång till en iterator för elementen, d.v.s. användaren får möjlighet att traversera samtliga element i samlingen. Det objekt som returneras måste vara av en klass
som implementerar interfacet Iterator<E> som innehåller operationerna hasNext() och
next(). Det är genom anrop av dessa operationer som traverseringen kan genomföras.
U 25.
a) Iterator<Person> itr = coll.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}
b) for (Person p : coll) {
System.out.println(p);
}
Interface, exceptions, generik, iteratorer
7
U 26. Lägg till metoden iterator som ska skapa och returnera ett iterator-objekt. Iteratorklassen (en klass som implementerar interfacet iterator måste också skrivas. Den placeras lämpligen som en inre klass inuti samlingsklassen.
public Iterator<E> iterator() {
return new ArrayIterator();
}
private class ArrayIterator implements Iterator<E> {
private int pos;
private ArrayIterator() {
pos = 0;
}
public boolean hasNext() {
return pos < size;
}
public E next() {
if (hasNext()) {
E item = theCollection[pos];
pos++;
return item;
} else {
throw new NoSuchElementException();
}
}
}
public void remove() {
throw new UnsupportedOperationException();
}
U 27. Då skulle man bara kunna iterera över elementen en enda gång (det finns ingen operation
för att sätta tillbaks positionen). Även om vi införde ytterligare en metod first() för att
sätta tillbaks positionen skulle det vara en dålig lösning. Man kan bara ha en position i
taget i samlingen. Det finns många algoritmer som behöver mer än en position för att t.ex
jämföra element med varandra.
Om vi i stället har en iteratorklass och en metod som returnerar ett objekt av denna
klass löses båda problemen. Vill man göra en ny iteration skapar man ytterligare ett objekt
av iteratorklassen genom att anropa operationen iterator. Olika instanser av klassen kan
ha olika positioner.