Java: Föreläsning 7

Innehåll i föreläsning 7

Några standardmetoder

I år (2007) hann vi med detta redan på föreläsning 6.

Metoder som är definierade i rotklassen Object, men kan omdefinieras:

Metoder som måste definieras om man implementerar gränssnittet Comparable: Klassen Telefonnummer, ur filen Datatest.java från föreläsning 5:
class Telefonnummer implements Comparable {
    private int landskod, riktnummer, abonnentnummer;

    public Telefonnummer(int landskod, int riktnummer, int abonnentnummer) {
        this.riktnummer = riktnummer;
        this.abonnentnummer = abonnentnummer;
        this.landskod = landskod;
    }
    public Telefonnummer(int riktnummer, int abonnentnummer) {
        this(46, riktnummer, abonnentnummer);
    }
    public Telefonnummer(int abonnentnummer) {
        this(46, 19, abonnentnummer);
    }

    public String toString() {
        return "+" + landskod + "-(0)" + riktnummer + "-" + abonnentnummer;
    }

    public int compareTo(Object o) {
        Telefonnummer rhs = (Telefonnummer)o;
        if (this.landskod < rhs.landskod)
            return -1;
        if (this.landskod > rhs.landskod)
            return 1;
        if (this.riktnummer < rhs.riktnummer)
            return -1;
        if (this.riktnummer > rhs.riktnummer)
            return 1;
        if (this.abonnentnummer < rhs.abonnentnummer)
            return -1;
        if (this.abonnentnummer > rhs.abonnentnummer)
            return 1;
        return 0;
    }

    public boolean equals(Object o) {
        if (!(o instanceof Telefonnummer))
            return false;
        Telefonnummer rhs = (Telefonnummer)o;
        return this.landskod == rhs.landskod &&
            this.riktnummer == rhs.riktnummer &&
            this.abonnentnummer == rhs.abonnentnummer;
    }

    public int hashCode() {
        int result = 17;
        result = 37 * result + landskod;
        result = 37 * result + riktnummer;
        result = 37 * result + abonnentnummer;
        return result;
    }
} // class Telefonnummer
Notera att två telefonnummer är lika med varandra (med equals) om de är likadana, inte bara om de är samma objekt.

Kort repetition av toString

Programmet Utskriftstest.java:
class Hamster {
    private String namn;
    public Hamster(String namn) { this.namn = namn; }
} // Hamster

class Hund {
    private String namn;
    public Hund(String namn) { this.namn = namn; }
    public String toString() { return "Hunden " + namn; }
} // Hund

public class Utskriftstest {
    public static void main(String[] args) {
        Hamster huey = new Hamster("Huey");
        System.out.println("huey: " + huey);

        Hund buster = new Hund("Buster");
        System.out.println("buster: " + buster);
    } // main
} // Utskriftstest
Utskrifter:
huey: Hamster@defa1a
buster: Hunden Buster

Mer om standardmetoderna (equals m fl)

Om jämförelser mellan objekt: Utdrag ur programmet Metodtest.java. Klassen Hund:
class Hund {
    private String namn;
    public Hund(String namn) { this.namn = namn; }
    public String toString() { return "Hunden " + namn; }
} // Hund
Programmet Metodtest.java fortsätter. Klassen Metodtest:
public class Metodtest {
    public static void main(String[] args) {
        // Skapa två hundar och skriv ut dem

        Hund buster = new Hund("Buster");
        Hund pluto = new Hund("Pluto");
        System.out.println("pluto: " + pluto);
        System.out.println("buster: " + buster);

        // Prova "=="-operatorn på hundarna

        System.out.println("buster == buster: " + (buster == buster));
        System.out.println("buster == pluto: " + (buster == pluto));

        // Skapa fler hundar och skriv ut dem

        Hund fido1 = new Hund("Fido");
        Hund fido2 = new Hund("Fido");
        Hund fido3 = fido2;

        System.out.println("fido1: " + fido1);
        System.out.println("fido2: " + fido2);
        System.out.println("fido3: " + fido3);

        // Prova "=="-operatorn på hundarna

        System.out.println("buster == fido1: " + (buster == fido1));
        System.out.println("fido1 == fido2: " + (fido1 == fido2));
        System.out.println("fido2 == fido3: " + (fido2 == fido3));

        System.out.println("buster == new Hund(\"Buster\"): " + (buster == new Hund("Buster")));
        System.out.println("new Hund(\"Karo\") == new Hund(\"Karo\"): " + (new Hund("Karo") == new Hund("Karo")));
Hundar. fido2 och fido3 refererar till samma ("==-lika") objekt.

Hundar

Utskrifter:

pluto: Hunden Pluto
buster: Hunden Buster
buster == buster: true
buster == pluto: false
fido1: Hunden Fido
fido2: Hunden Fido
fido3: Hunden Fido
buster == fido1: false
fido1 == fido2: false
fido2 == fido3: true
buster == new Hund("Buster"): false
new Hund("Karo") == new Hund("Karo"): false
Slutsats: En Hund-instans är bara "==" med sig själv. Och i Java kan man inte omdefiniera operatorer, så det går inte att ändra.

Programmet Metodtest.java fortsätter. Nu ska vi prova equals-metoden på samma hundar.

        System.out.println("buster.equals(buster): " + buster.equals(buster));
        System.out.println("buster.equals(pluto): " + buster.equals(pluto));
        System.out.println("fido1.equals(fido1): " + fido1.equals(fido1));
        System.out.println("fido1.equals(fido2): " + fido1.equals(fido2));
        System.out.println("fido2.equals(fido3): " + fido2.equals(fido3));
Utskrifter:
buster.equals(buster): true
buster.equals(pluto): false
fido1.equals(fido1): true
fido1.equals(fido2): false
fido2.equals(fido3): true
Slutsats: En Hund-instans är bara "equals" med sig själv. I Java kan man definiera en ny equals-metod för en klass, men det har vi inte gjort för Hund-klassen.

Tänk så här: Två hundar är olika hundar, även om de har samma namn!

Programmet Metodtest.java fortsätter. Nu ska vi prova <-operatorn och compareTo-metoden på hundarna.

        System.out.println("buster < pluto: " + (buster < pluto));
        // Ger kompileringsfel:
        // Metodtest.java:56: operator < cannot be applied to Hund,Hund

        System.out.println("buster.compareTo(pluto): " + (buster.compareTo(pluto)));
        // Ger kompileringsfel:
        // Metodtest.java:81: cannot resolve symbol
        // symbol  : method compareTo (Hund)
Slutsats: Man kan inte alls storleksjämföra hundar, vare sig med < och de andra jämförelseoperatorerna, eller med compareTo. (Såklart! Vi har ju inte skrivit någon compareTo för klassen Hund!)

Klassen Hundras:

class Hundras {
    protected final String namn;
    public Hundras(String namn) { this.namn = namn; }
    // Hitta felet:
    // public Hundras(String name) { this.namn = namn; }
    public String toString() { return "Hundrasen " + namn; }

    public int compareTo(Object o) {
        Hundras rhs = (Hundras)o;
        return this.namn.compareTo(rhs.namn);
    }

    public boolean equals (Object o) {
        if (!(o instanceof Hundras))
            return false;
        Hundras rhs = (Hundras)o;
        return this.namn == rhs.namn;
    }
} // Hundras
Klassen Hundras har en equals-metod och en compareTo-metod. Programmet Metodtest.java fortsätter. Nu ska vi prova ==-operatorn på några hundraser.
        Hundras pudel = new Hundras("pudel");
        Hundras pitbull = new Hundras("pitbull");

        System.out.println("pudel: " + pudel);
        System.out.println("pitbull: " + pitbull);
        System.out.println("pudel == pudel: " + (pudel == pudel));
        System.out.println("pudel == pitbull: " + (pudel == pitbull));

        Hundras schäfer1 = new Hundras("schäfer");
        Hundras schäfer2 = new Hundras("schäfer");

        System.out.println("pudel == schäfer1: " + (pudel == schäfer1));
        System.out.println("schäfer1 == schäfer2: " + (schäfer1 == schäfer2));
Hundraser. schäfer1 och schäfer2 refererar inte till samma objekt, men till likadana ("equals-lika") objekt.

Hundraser

Utskrifter:

pudel: Hundrasen pudel
pitbull: Hundrasen pitbull
pudel == pudel: true
pudel == pitbull: false
pudel == schäfer1: false
schäfer1 == schäfer2: false

Slutsats: Även en Hundras-instans är bara "==" med sig själv.

Nu ska vi prova equals-metoden på samma hundraser:

        System.out.println("pudel.equals(pudel): " + pudel.equals(pudel));
        System.out.println("pudel.equals(pitbull): " + pudel.equals(pitbull));
        System.out.println("schäfer1.equals(schäfer1): " + schäfer1.equals(schäfer1));
        System.out.println("schäfer1.equals(schäfer2): " + schäfer1.equals(schäfer2));
Slutsats: En Hundras-instans är equals med sig själv, och med alla andra Hundras-instanser som har samma namn.

Kanske ska man tänka så här: Det kan inte finnas två olika hundraser som har samma namn.

Utskrifter:

pudel.equals(pudel): true
pudel.equals(pitbull): false
schäfer1.equals(schäfer1): true
schäfer1.equals(schäfer2): true

Programmet Metodtest.java fortsätter. Nu ska vi prova <-operatorn och compareTo-metoden på hundraserna.

        // System.out.println("pudel < pitbull: " + (pudel < pitbull));
        // Ger kompileringsfel:
        // Metodtest.java:142: operator < cannot be applied to Hundras,Hundras

        System.out.println("pudel.compareTo(pitbull): " + (pudel.compareTo(pitbull)));
        System.out.println("schäfer1.compareTo(schäfer2): " + (schäfer1.compareTo(schäfer2)));

        // System.out.println("pudel.compareTo(pluto): " + (pudel.compareTo(pluto)));
        // Ett undantag kastas:
        // Exception in thread "main" java.lang.ClassCastException
        //         at Hundras.compareTo(Metodtest.java:28)
Utskrifter:
pudel.compareTo(pitbull): 12
schäfer1.compareTo(schäfer2): 0
Programmet Metodtest.java fortsätter:
        // Men hallå där! SAMMA namn? Är "equals"-metoden verkligen rätt?

        Hundras labrador1 = new Hundras(new String("Labrador"));
        Hundras labrador2 = new Hundras(new String("Labrador"));
        System.out.println("labrador1 == labrador2: " + (labrador1 == labrador2));
        System.out.println("labrador1.equals(labrador2): " + labrador1.equals(labrador2));
Fortsättning på körexemplet:
labrador1 == labrador2: false
labrador1.equals(labrador2): false
Hundraser, mer detaljerat. Fältet namn i de objekt som refereras av schäfer1 och schäfer2 refererar till samma sträng. Fältet namn i de objekt som refereras av labrador1 och labrador2 refererar inte till samma sträng, men till likadana strängar.

Hundraser, detaljerat

Lärdom: Använd inte == för att jämföra om två strängar är lika. Använd equals i stället. Använd == på strängar bara om man, av någon anledning, vill kolla om det verkligen är samma strängobjekt.

Klassen Hundras2, som använder equals i stället för == för att jämföra namn-strängarna:

class Hundras2 extends Hundras {
    public Hundras2(String namn) { super(namn); }
    public boolean equals (Object o) {
        if (!(o instanceof Hundras))
            return false;
        Hundras rhs = (Hundras)o;
        return this.namn.equals(rhs.namn);
    }
} // Hundras2
Programmet Metodtest.java fortsätter:
        // Kolla om det blev bättre med "equals" i st f "==" i "compareTo"

        labrador1 = new Hundras2(new String("Labrador"));
        labrador2 = new Hundras2(new String("Labrador"));
        System.out.println("labrador1 == labrador2: " + (labrador1 == labrador2));
        System.out.println("labrador1.equals(labrador2): " + labrador1.equals(labrador2));
Utskrifter:
labrador1 == labrador2: false
labrador1.equals(labrador2): true
Programmet Metodtest.java fortsätter:
        // Prova det där med hashCode

        HashSet<Hundras> hundraser = new HashSet<Hundras>();
        hundraser.add(labrador1);
        System.out.println("hundraser.contains(labrador1): " + hundraser.contains(labrador1));
        System.out.println("hundraser.contains(labrador2): " + hundraser.contains(labrador2));
Utskrifter:
hundraser.contains(labrador1): true
hundraser.contains(labrador2): false
Klassen Hundras3, som dessutom har metoden hashCode:
class Hundras3 extends Hundras2 {
    public Hundras3(String namn) { super(namn); }
    public int hashCode() { return 42; }
} // Hundras2
Programmet Metodtest.java fortsätter:
        // Kolla om det blev bättre med en "hashCode"-metod

        hundraser.remove(labrador1);
        labrador1 = new Hundras3(new String("Labrador"));
        labrador2 = new Hundras3(new String("Labrador"));
        hundraser.add(labrador1);
        System.out.println("hundraser.contains(labrador1): " + hundraser.contains(labrador1));
        System.out.println("hundraser.contains(labrador2): " + hundraser.contains(labrador2));

        // Slutsats: Om man gör en egen "equals",
        // ska man också göra en egen "hashCode"

    } // main
} // Metodtest
Slutet på utskrifterna:
hundraser.contains(labrador1): true
hundraser.contains(labrador2): true

En lärdom: Metoderna equals, hashCode och compareTo hör ihop. Om man skrivit en av dem, bör man ofta skriva alla tre.

Inlämningsuppgift 2

Bild på arkitekturen

Se instruktionen.

Körexempel med den gamla textklienten:

Uppkopplad mot server
ABC123   BBC123   AAC123   BBD123   ABC223   
BBC323   AAC723   BBD823   

A(llt)/M(edel)/N(ytt)/T(a bort)/E(xit) => 
A
Skriv regnr: AAC123
Regnr: AAC123
Märke: Volvo
År: 96
Nypris: 160000

ABC123   BBC123   AAC123   BBD123   ABC223   
BBC323   AAC723   BBD823   

A(llt)/M(edel)/N(ytt)/T(a bort)/E(xit) => 
N
Skriv regnr: XYZ123
Skriv märke: Hummer
Skriv nypris: 1700000
Skriv år: 2003
XYZ123 inlagd. Det gick BRA!

ABC123   BBC123   AAC123   BBD123   ABC223   
BBC323   AAC723   BBD823   XYZ123   

A(llt)/M(edel)/N(ytt)/T(a bort)/E(xit) => 
E

Javadoc

Programmet Hund.java:
/** En instans av klassen Hund representerar en enskild hund. */
public class Hund {
    /** Variabeln namn innehåller hundens namn.
     *  Flera hundar kan ha samma namn.
     */
    private String namn;
    public Hund(String namn) { this.namn = namn; }
    public String toString() { return "Hunden " + namn; }
} // Hund
Kör programmet javadoc, som följer med Suns SDK, och som finns på samma ställe som javac, java med flera.

Skriv: javadoc Hund.java

Resultatet blir en HTML-sida med dokumentation för klassen.

Ett annat exempel: För ClientThread.java ger javadoc denna dokumentationssida.

Ett exempel till finns här, hos Sun. På samma webbsida finns mycket mer information om Javadoc.


Thomas Padron-McCarthy (thomas.padron-mccarthy@tech.oru.se), 4 december 2007