Ylös Edellinen Seuraava Otsikkosivu Hakemisto Sisällys

9.6 Object-luokan metodien korvaaminen

Jos Javassa ei peritä luokkaa mistään, niin se periytyy aina Object-luokasta. Näin siksi, että kaikki oliot saadaan samaan hierarkiaan ja voidaan esimerkiksi tallentaa samaan tietorakenteeseen. Käytännössä tämä ei ole kovin kätevää, sillä silloin tietorakenteessa olevilla olioilla on käytössä vain Object-luokan metodit. Jotta olioilla voitaisiin tehdäkin jotakin, pitää niiden tyyppi muuntaa vastaamaan niiden varsinaista luokkaa.

Object-luokassa on kuitenkin muutama tärkeä metodi, joiden olemassa olosta ohjelmoijan on hyvä olla tietoinen:

	Object clone();             // tekee oliosta itsestään kopion
	boolean equals(Object obj); // vertaa oliota toiseen olioon
	int hashCode();             // palauttaa olioon liityvän "lajitteluavaimen"
	String toString();          // palauttaa olion merkkijonona

olioalk\AikaF.java - luokka joka toteuttaa Object

	/**
	 * Luokka toteuttamaan sovitun julkisen rajapinnan ja Object-
	 * luokan metodeja. 
	 * @author  Vesa Lappalainen
	 * @version 1.0, 01.02.2003
	 */
	public class AikaF implements AikaRajapinta {
	
	  private int h, m;
	
	  /**
	   * Asettaa uuden ajan ja pitää huolen että aika on aina oikeaa muotoa.
	   * @param h asetettavat tunnit
	   * @param m asetettavat minuutit
	   */
	  public void aseta(int h,int m) {
	    this.h = h; this.m = m; lisaa(0);
	  }
	
	  public AikaF() { this.h = 0; this.m = 0; }
	
	  /**
	   * Asettaa uuden ajan ja pitää huolen että aika on aina oikeaa muotoa.
	   * @param h asetettavat tunnit
	   */
	  public AikaF(int h) {
	    aseta(h,0);
	  }
	
	  /**
	   * Alustaa ajan
	   * @param h tunnit
	   * @param m minuutit
	   */
	  public AikaF(int h,int m) { // Muodostaja
	    aseta(h,m);
	  }
	
	  /**
	   * Tulostaa ajan muodossa 15:05
	   * @param lf tulostetaanko rivinvaihto vai ei
	   */
	  public void tulosta(boolean lf) {
	    System.out.print(toString());
	    if ( lf ) System.out.println();
	  }
	
	  /**
	   * Tulostaa ajan muodossa 15:05 sekä aina rivinvaihdon
	   */
	  public void tulosta() {
	    tulosta(true);
	  }
	
	  /**
	   * Lisää aikaan valitun minuuttimäärän
	   * @param lisa_min lisättävä minuuttimäärä
	   */
	  public void lisaa(int lisa_min)  {
	    int yht_min = h * 60 + m + lisa_min;
	    h = yht_min / 60;
	    m = yht_min % 60;
	  }
	
	  public static void lisaa(AikaF aika,int lisa_min) {
	    int yht_min = aika.h * 60 + aika.m + lisa_min;
	    aika.h = yht_min / 60;
	    aika.m = yht_min % 60;
	  }
	
	  public int getH() { return h; }
	  public int getM() { return m; }
	
	  public String toString() {
	    return "" + h + ":" + (m<10?"0":"")+m;
	  }
	
	  public boolean equals(Object o) {
	    if ( !(o instanceof AikaRajapinta) ) return false;
	    AikaRajapinta a = (AikaRajapinta)o;
	    return getH() == a.getH() && getM() == a.getM();
	  }
	
	  public Object clone() {
	    return new AikaF(getH(),getM());
	  }
	
	  public int hashCode() {
	    return 3600*getH() + 60*getM();
	  }
	
	  public static void main(String[] args) {
	    AikaF a1 = new AikaF();
	    AikaF a2 = new AikaF(13);
	    AikaF a3 = new AikaF(14,25);
	
	    a1.tulosta();  a2.tulosta();  a3.tulosta();
	    a1.aseta(12,15); a2.aseta(16,-15);
	    a1.tulosta(); a2.tulosta();
	
	    lisaa(a1,55); a1.tulosta();
	
	    System.out.println("Tunnit = " + a1.getH());
	    System.out.println(a1.toString());
	  }
	
	}

Esimerkiksi tekemällä metodi

	  public String toString() {
	    return "" + h + ":" + (m<10?"0":"")+m;
	  }

vältämme kaikki tulostukseen liittyvät ongelmat. Jos halutaan verrata kahta Aika-oliota keskenään, kannattaa kirjoittaa equals-metodi.

	  public boolean equals(Object o) {
	    if ( !(o instanceof AikaRajapinta) ) return false;
	    AikaRajapinta a = (AikaRajapinta)o;
	    return getH() == a.getH() && getM() == a.getM();
	  }

equals-metodia kirjoitettaessa on oltava huolellinen, sillä parametrina saattaa tulla oikean tyyppinen olio tai sitten väärän tyyppinen olio. equals-metodin pitää toteuttaa seuraavat ominaisuudet:

	Olkoon seuraavassa a1,a2 ja a3 kolme luokan oliota.
	reflektiivisyys:  a1.equals(a1) pitää olla aina tosi
	symetrisyys:      a1.equals(a2) == a2.equals(a1)
	transitiivisuus:  a1.equals(a2) = true; a2.equals(a3) = true;  => a1.equals(a3) = true;
	luonnollisesesti toistuvien equals kutsujen pitää palautta samoille olioille
	sama arvo, mikäli olioiden samuuteen vaikuttava tila ei muutu

Jos luokkaan toteutetaan equals-metodi, on siihen toteutettava myös hajautusarvo. Javan tietorakenteet tarvitsevat hajautusarvoa. Hajautusarvon täytyy palauttaa sama luku olioille, jotka ovat equals-vertailussa saman arvoisia. Mutta kaksi eriarvoistakin oliota saa palauttaa saman hajautusarvon. Meidän tapauksessamme hajautusarvoksi voidaan valita vaikkapa sekunnit vuorokauden alusta:

	  public int hashCode() {
	    return 3600*getH() + 60*getM();
	  }

Lisäksi monessa tilanteessa tarvitaan oliosta samanlainen kopio. Tätä varten toteutetaan clone-metodi:

	  public Object clone() {
	    return new AikaF(getH(),getM());
	  }

Tehtävä 9.16 equals toString avulla

Toteuta equals-metodi toString-metodin avulla. Arvioi ratkaisun tehokkuutta.

Tehtävä 9.17 equals AikaSek-luokkaan

equals-metodiin tulee ongelmia toteutettaessa AikaSek-luokkaa. Mieti mitä.

Tehtävä 9.18 AikaSek perimällä.

Esimerkissä AikaSekF on toteutettu sekunnit sisältävä aikaluokka koostamalla. Kokeile miten nyt onnistuu perintä AikaF-luokasta ja mitä metodeja pitää korvata.

Tehtävä 9.19 Vertailu

Tutki dokumenteista rajapintaa Comparable ja muuta luokat AikaF ja AikaSekF toteuttamaan tuo rajapinta.


Ylös Edellinen Seuraava Otsikkosivu Hakemisto Sisällys