Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: [Java] Luokat ja ArrayList

Sivun loppuun

stt-73 [07.06.2012 19:12:38]

#

Hei
Mikä ohjelmassa on vikana.

Tarkoitus on tallentaa KayttoLiittyma -luokassa annetut tiedot Rek -luokassa olevaan ArrayList:iin ja tulostaa ne kun päättää silmukan syötteellä "q".


Käyttöliittymä - luokka

import java.util.*;

public class KayttoLiittyma {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub


		String syote;
		int ikasi;
		String nimi = null;
		int ika = 0;


		Scanner lukija = new Scanner(System.in);

		do {
		    System.out.println("Anna nimi: ");
		    syote = lukija.next();
		    System.out.println("Ja sitten ikäsi: ");
		    ikasi = lukija.nextInt();


		} while(!(syote.equals("q")));

		Henkilo uusi = new Henkilo(syote, ikasi);
		Henkilo uusiHenkilo = new Henkilo(nimi, ika);
		Rek rekisteri = new Rek();

		rekisteri.lisaa(uusiHenkilo);

		uusi.setNimi(syote);
		uusi.setIka(ikasi);

		Henkilo.tulosta(syote, ikasi);

		Rek.tulostaLista();

		}


}

Henkilö - luokka

public class Henkilo {
	private String nimi;
	private int ika;

	public Henkilo(String nimi, int ika) {
		this.nimi = nimi;
		this.ika = ika;
	}

	public Henkilo(Henkilo henkilo) {
		// TODO Auto-generated constructor stub
	}

	public String getNimi() {
		return nimi;
	}
	public int getIka() {
		return ika;
	}

	public void setNimi(String nimi) {
		this.nimi = nimi;
	}
	public void setIka(int ika) {
		this.ika = ika;
	}



	static void tulosta(String nimi, int ika) {

		System.out.println(nimi + " " + ika);
		return;

	}

}

Rek - luokka

import java.util.*;
public class Rek {
	/** Säiliönä toimiva lista*/
	public static ArrayList<Henkilo> lista;

	/**
	 * Konstruktori
	 */
	public Rek() {
	    lista = new ArrayList<Henkilo>();
	}
    /**
     * Metidi lisää henkilön listaan
     * @param uusiHenkilo
     */
	public void lisaa(Henkilo uusiHenkilo) {
		lista.add(new Henkilo(uusiHenkilo));
		}

	public static Henkilo tulostaLista() {
		for (Henkilo uusiHenkilo : lista) {
			System.out.println(uusiHenkilo);
			return uusiHenkilo;
		}
		return null;


	}

}

jukkah [07.06.2012 21:10:54]

#

Mikä kohta siinä ei toimi? Virheilmoitus, ikuinen silmukka...

stt-73 [07.06.2012 21:15:28]

#

Meneekö syötteet listaan?

Kirjoitti mitä tahansa tulostus on aina sama
Tulostus on: Henkilo@1df3bec

jukkah [07.06.2012 21:27:53]

#

stt-73 kirjoitti:

Henkilo@1df3bec

Laita Henkilo-luokkaan tämä:

public String toString() {
    return nimi + " " + ika;
}

stt-73 [07.06.2012 21:36:23]

#

Se poisti sen tulostuksen, mutta tilalle tuli:
null 0
Taitaa olla myös KayttoLiittyma -luokan alustukset pielessä

jukkah [07.06.2012 21:43:29]

#

stt-73 kirjoitti:

null 0

Rek.lisaa-metodissa luodaan itse asiassa Henkilo arvoilla null ja 0 (se kutsuu Henkilön rakentajista sitä, jossa on vain kommentti). Lisaa-metodissa saat parametrina jo Henkilon, joten älä luo uutta ilmentymää vaan laita olemassa oleva suoraan listaan:

lista.add(uusiHenkilo);

Edit. Et muuten tarvitse mihinkään sitä kommentin sisältävää Henkilon rakentajaa yllä olevan muutoksen jälkeen.

Metabolix [07.06.2012 22:14:12]

#

jukkah, miksi heittelet yksittäisiä korjausyrityksiä, kun on selvää, ettei tuota koodia korjata yhdellä tai kahdella muutoksella?

Vikoja on aika monta. Silmukassa et lisää ollenkaan käyttäjiä listaan, vaan lisäät yhden ainoan käyttäjän vasta silmukan jälkeen. Listan käsittelyssä luot jostain syystä uuden henkilön muodostimella, joka ei aseta yhtään jäsentä; sinun pitäisi laittaa listaan se parametrina annettu henkilö eikä luoda uutta. Lisäksi koko main-funktion sisältö on aivan älytön: sinulla on kaksi henkilöä, kaksi nimeä ja kaksi ikää, joista toiset jäävät täysin tyhjiksi, ja lopussa vielä tulostat staattisella metodilla yhden henkilön tiedot suoraan muuttujista (eli et oikeasti käytä vastaavaa oliota ollenkaan).

Mene lukemaan jokin Java-opas. Älä etene, ennen kuin ihan varmasti osaat edelliset asiat kunnolla. Nyt näyttää, ettet osaa vielä mitään kunnolla.

jukkah [07.06.2012 22:41:03]

#

Metabolix kirjoitti:

jukkah, miksi heittelet yksittäisiä korjausyrityksiä, kun on selvää, ettei tuota koodia korjata yhdellä tai kahdella muutoksella?

Käytämme erilaista taktiikkaa tässä asiassa. Minulle helpoin tapa olisi laittaa vain toimiva koodi näytille, mutta oppiminen jää aika vähäiseksi sillä tavalla ottaen huomioon oppijan tämänhetkisen osaamisen. Toisaalta en usko, että jos kerron kaikki ongelmakohdat kerralla, siitä monikaan ottaisi mitään selvää (puhumattakaan, että sen perusteella osaisi korjata koodiaan). Jos taas sanon, että ihan huono koodi, se ei rohkaise yrittämään.

Metabolix kirjoitti:

Mene lukemaan jokin Java-opas. Älä etene, ennen kuin ihan varmasti osaat edelliset asiat kunnolla. Nyt näyttää, ettet osaa vielä mitään kunnolla.

Luultavasti käytössä on jokin Java-opas. Muistan itsekin kahlanneeni jokin vuosi sitten Hello, World!in jälkeen liian nopeasti. Normaalia kehittymistä... Uskallanpa väittää, että yksi tiedonmurunen on saattanut jäädä päähän toString-metodista.

Sitten vähän kyseenalaistetusta taktiikastani: käyn metodi / yksi asia kerrallaan ongelmakohdat läpi "syvältä pinnalle" eli ensin mallit kuntoon sitten kontrollerit ja viimeisenä näkymä. Sillä tavalla tulee chat-tyylistä kirjoittelua, mutta ei kai se ketään haittaa. :)

Edit. Sanopa muuten Metabolix (tai joku muu) kokeneempana jokin hyvä suomenkielinen Java-opas (huonoja löytyy pitkä lista jos Googlaisi), joka on kaikkien saatavilla netissä. Voisin itsekin vilkaista sitä.

stt-73 [07.06.2012 23:02:32]

#

Kyllä on yksi jos toinenkin java-opas käytössä, mutta luetun ymmärtämisessä on joskus ongelmia :)
Tällä hetkellä on luokat ja niiden väliset olio-yhteydet haasteena ja niiden ymmärtämisessä aika paljon ongelmia.
Yhdessä luokassa tapahtuva silmukasta syötteen siirtäminen listaan onnistuu hyvin :D mutta kun on useampi luokka silloin alkaa ongelmat.

Metabolix [08.06.2012 15:40:54]

#

jukkah kirjoitti:

Minulle helpoin tapa olisi laittaa vain toimiva koodi näytille,

Voisin ymmärtää tämän, jos laittaisit sen toimivan koodin, mutta tähän mennessä et ole laittanut sellaista.

jukkah kirjoitti:

Sitten vähän kyseenalaistetusta taktiikastani: käyn metodi / yksi asia kerrallaan ongelmakohdat läpi "syvältä pinnalle" eli ensin mallit kuntoon sitten kontrollerit ja viimeisenä näkymä. Sillä tavalla tulee chat-tyylistä kirjoittelua, mutta ei kai se ketään haittaa. :)

En toki voi sanoa tätä omakohtaisesti, mutta on varmaan tylsää vastapuolen kannalta, kun aina tulee vastaus ilman kunnollisia selityksiä ja herää toivomus, että tässä on nyt se viimeinen tarvittava korjaus, vaikka ehdotettu muutos ei oikeasti edes paranna tilannetta aloittelijan näkökulmasta juuri lainkaan (eli ohjelma ei vieläkään toimi).

jukkah kirjoitti:

Sanopa muuten – – kokeneempana jokin hyvä suomenkielinen Java-opas

Tuo on tavallaan outo toivomus, koska kokeneet ohjelmoijat eivät yleensä lue alkeisoppaita ja saattavat siis tietää niistä vähemmän kuin aloittelijat. Sattumoisin tiedän kuitenkin, että esimerkiksi Helsingin yliopiston kesäkurssien materiaalit ovat hyviä: Ohjelmoinnin perusteet ja Ohjelmoinnin jatkokurssi. Tietenkään näillä ei pääse huimaavan pitkälle Javan kirjastojen ja oikean sovelluskehityksen suhteen, mutta jos nuo asiat opettelee, ei ainakaan tarvitse enää törttöillä alkeiden kanssa.

stt-73 kirjoitti:

Yhdessä luokassa tapahtuva silmukasta syötteen siirtäminen listaan onnistuu hyvin :D mutta kun on useampi luokka silloin alkaa ongelmat.

En ymmärrä, miten luokkien lisääminen kuvioon voi sekoittaa silmukan tavallisen käytön. Se, että lisäät listaan Henkilo-olioita String-olioiden sijaan, ei muuta silmukkaa miksikään.

jukkah [08.06.2012 20:29:34]

#

Metabolix kirjoitti:

jukkah kirjoitti:

Minulle helpoin tapa olisi laittaa vain toimiva koodi näytille,

Voisin ymmärtää tämän, jos laittaisit sen toimivan koodin, mutta tähän mennessä et ole laittanut sellaista.

Öö, nyt ei leikannut, mitä vaikeaa tässä oli. Kolmen luokan kirjoittaminen vie noin 10 min + ehkä toinen samanlainen jonkun yksittäisen bugin korjaamiseen. Sitten koodin voisi laittaa näytille "tässä koko roska"-tyyliin.

Metabolix kirjoitti:

jukkah kirjoitti:

Sitten vähän kyseenalaistetusta taktiikastani: käyn metodi / yksi asia kerrallaan ongelmakohdat läpi "syvältä pinnalle" eli ensin mallit kuntoon sitten kontrollerit ja viimeisenä näkymä. Sillä tavalla tulee chat-tyylistä kirjoittelua, mutta ei kai se ketään haittaa. :)

En toki voi sanoa tätä omakohtaisesti, mutta on varmaan tylsää vastapuolen kannalta, kun aina tulee vastaus ilman kunnollisia selityksiä ja herää toivomus, että tässä on nyt se viimeinen tarvittava korjaus, vaikka ehdotettu muutos ei oikeasti edes paranna tilannetta aloittelijan näkökulmasta juuri lainkaan (eli ohjelma ei vieläkään toimi).

Niin no, kannattaisi kysyä, kun voi, että miten toinen haluaa...

Eli stt-73, rastia ruutuun, mitä haluat: (jäljellä noin 6 korjattavaa kohtaa)
a) kokonainen toimiva koodi näytille
b) lista korjattavista kohdista ja niiden korjauksista samaan viestiin
c) jatketaan samaan tyyliin, kuin eilen illalla
d) joku muu, mikä?

Metabolix kirjoitti:

stt-73 kirjoitti:

Yhdessä luokassa tapahtuva silmukasta syötteen siirtäminen listaan onnistuu hyvin :D mutta kun on useampi luokka silloin alkaa ongelmat.

En ymmärrä, miten luokkien lisääminen kuvioon voi sekoittaa silmukan tavallisen käytön. Se, että lisäät listaan Henkilo-olioita String-olioiden sijaan, ei muuta silmukkaa miksikään.

Minä taas ymmärrän, monesti "+1-1"-jutut saavat kokemattoman tipahtamaan kärryltä. Se vain ottaa oman aikansa, että sisäistää olio-paradigman. Joillakin menee enemmän toisilla vähemmän aikaa (itselläni meni puoli vuotta aktiivisella harjoittelulla).

stt-73 [09.06.2012 18:38:27]

#

Voisi käydä läpi virhekohdat ja selitys mikä on virheenä.
Joko yhdessä viestissä tai useammassa, sillä ei ole niin päljon väliä.
Pieni ohjelma, ei pitäisi mennä pitkään kummallakaan tavalla.
Metabolix:en antamiin oppaisiin en ole aiemmin törmännyt.Ne näyttävät erittäin hyviltä oppailta. Pitää ottaa ne käyttöön.

jukkah [09.06.2012 23:27:02]

#

Jatketaan Rek-luokasta. Tässä kaikki kolme korjausta siihen luokkaan selitysten kera:

private ArrayList<Henkilo> lista;

Tässä tapauksessa listaa tulee saada käsitellä vain Rek-luokan "nykyisen" olion. Static-avainsanaa ei pidä käyttää tässä yhteydessä, koska jos tehtäisiin kaksi Rek-oliota, molemmilla olisi tarkoitus olla oma listansa. Public-avainsanaa ei pidä käyttää tässä yhteydessä, koska listan manipulointi tapahtuu vain Rek-luokan metodeilla, joita voidaan kutsua muista luokista.

// Tämän olet jo luultavasti korjannut.
public void lisaa(Henkilo uusiHenkilo) {
    lista.add(uusiHenkilo);
}

Tässä ei tarvita uuden Henkilo-olion luontia, koska lisättävä henkilö saadaan jo parametrina. Eri asia on, jos parametrina saataisiin nimi ja ikä, silloin pitäisi luoda uusi henkilö, mutta ei nyt.

public void tulostaLista() {
    // Käydään läpi lista yksi henkilö kerrallaan.
    for (Henkilo henkilo : lista) {
         // Tulostetaan henkilön merkkijonoesitys.
        System.out.println(henkilo);
    }
}

TulostaLista-metodi ei tarvitse static-avainsanaa, koska tarkoitus on käsitellä vain nykyisen olion listaa (joka ei myöskään ole staattinen). Lisäksi ole kovin järkevää, että listan tulostava metodi antaisi paluuarvoksi Henkilo-olion. (Esim. Listan viimeisen henkilön hakeminen ei liity mitenkään listan tulostamiseen. Se on selvästi oma toimintonsa.)

Kun metodin paluuarvon tyyppi on void (eli ei mitään), return-lausetta ei tarvita metodin loppuun (paluuarvon ollessa muu kuin void, se on aina pakollinen, joten tätä ei lasketa virheeksi). Listan sisässä oleva return-lause tulee myös poistaa. Mikäli se jätettäisiin siihen (return; olisi validia Javaa void-tyyppisessä metodissa), listasta tulostettaisiin vain ensimmäinen henkilö, ja loput jäisivät tulostumatta. Sehän ei ollut tarkoitus.

Miten olio tulostetaan komentoriville (tämä ei aukene ihan helposti)? Listan tulostuksessa tulostetaan komentoriville Henkilo-olioita. Ne eivät ole merkkijonoja millään tavalla, joten ne pitää muuttaa ensin merkkijonoiksi. Kaikki oliot ovat Object-luokan ilmentymiä (Object on Javan kantaluokka), ja Object-luokassa määritellään toString-metodi, joka antaa olion merkkijonoesityksen. Helpommin sanottuna kaikilla olioilla on toString-metodi riippumatta siitä määriteltiinkö sitä ko. luokassa vai ei.

Object-luokassa metodi on toteutettu siten, että merkkijono voi olla vaikka "Henkilo@1df3bec". Henkilo-luokka voi ylikirjoittaa toString-metodin, jolloin tulostus voi olla vaikka "Salamanteri 123". Jos tehdään Tyontekija-luokka, joka perii Henkilo-luokan (eli työntekijä on aina henkilö), ja se ylikirjoittaisi myös Henkilo-luokan toString-metodin, tulostus voi olla vaikka "Ohjelmoija, Salamanteri 123".

Kaikista näistä aiheista löytyy pitkästi tekstiä Metabolixin mainitsemista oppaista.

stt-73 [10.06.2012 19:08:05]

#

Kiitos jukkah, juuri tällaista opastusta tarkoitin

jukkah [11.06.2012 18:32:38]

#

Katsotaanpa sitten Henkilo-luokkaa:

// Tämän olet jo varmaan poistanut
public Henkilo(Henkilo henkilo) {
    // TODO Auto-generated constructor stub
}

Tämä ei varsinaisesti ole virhe, koska IDE on ehdottanut tätä toisen virheen korjaukseksi. Tarkastellaan kuitenkin vähän tällaista koodia. Uskallan väittää, ettei ole olemassa sellaista tapausta, johon tätä pitäisi käyttää. Jos on tarkoitus tehdä yhdestä oliosta toinen samanlainen, kyseessä on kloonaus, jolle Javassa on omat keinonsa.

Toinen vähän samantapainen tilanne on, kun luodaan nelikulmio tietynlaisella venytyksellä. Silloin voitaisiin tehdä nelikulmiolle rakentaja Nelikulmio(Nelikulmio alkuperainen, Venytys venytys). Tämä ajaa ihan saman asian kuin Nelikulmio-luokan metodi venyta(Venytys venytys), joka palauttaa uuden nelikulmion. Venyta-metodi on tässä tapauksessa parempi vaihtoehto (selitän tarvittaessa, miksi).

// Tämän olet jo korjannutkin
@Override
public String toString() {
    return nimi + " " + ika;
}

Tulosta-metodi kuuluu korvata toString()-metodilla. Koskaan ei pitäisi kovakoodata jokaiseen luokkaan tulostustoimintoa (se tekee kaksikin asiaa: muotoilee merkkijonoesityksen ja tulostaa sen komentoriville). Näin siksi, että jos luokan merkkijonoesitystä ei olekaan tarkoitus laittaa suoraan komentoriville, vaan joku toinen luokka tarvitsee sitä.

(ks. Henkilo-Tyontekija-esimerkki edellisen viestini lopussa) Tyontekija-luokan toString-metodissa voidaan hyödyntää Henkilo-luokan toString-metodia. Tyontekija-olio on aina myös Henkilo-olio, joten Tyontekija-luokasta voidaan kutsua Henkilo-luokan toString-metodia (yliluokan metodin kutsuminen ja ylikirjoitetun metodin kutsuminen). Tämä tarkoittaa sitä, että Tyontekija-luokka voi hyödyntää kaikkia Henkilo-luokan toimintoja (pois lukien private-avainsanalla määritellyt). Se tarkoittaa myös sitä, että Tyontekija-luokassa ei tarvitse määritellä get- ja set-metodeja nimelle ja iälle, koska ne ovat jo Henkilo-luokassa. Riittää, että Tyontekija-luokassa on vain se osa toiminnallisuudesta, joka laajentaa tai rajoittaa olion käyttäytymistä (henkilö ei voi saada palkkaa, mutta työntenkijä voi).

stt-73 [11.06.2012 18:49:45]

#

Kiitos, tähänastiset toimenpiteet suoritettu :)

jukkah [11.06.2012 19:33:48]

#

Viimeisenä vielä KayttoLiittyma-luokka:

import java.util.Scanner;

public class KayttoLiittyma {

    private Scanner lukija;
    private Rek rekisteri;

    public KayttoLiittyma() {
        this(new Scanner(System.in));
    }

    public KayttoLiittyma(Scanner lukija) {
        this.lukija = lukija;
        rekisteri = new Rek();
    }

    public void kaynnista() {
        lueHenkilotRekisteriin();
        tulostaRekisterinSisalto();
    }

    private void lueHenkilotRekisteriin() {
        while (lueHenkiloRekisteriin()) {
        }
    }

    private boolean lueHenkiloRekisteriin() {
        Henkilo henkilo = lueHenkilo();

        // henkilo == null, kun nimeksi annettiin "q".
        if (henkilo != null) {
            // Jos lukeminen onnistui, lisätään henkilö rekisteriin.
            rekisteri.lisaa(henkilo);
        }

        // Kerrotaan, onnistuiko lukeminen.
        return henkilo != null;
    }

    private void tulostaRekisterinSisalto() {
        System.out.println("Rekisterin henkilöt ovat:");

        rekisteri.tulostaLista();
    }

    private Henkilo lueHenkilo() {
        System.out.println("Lisää uusi henkilö");

        // Jos nimeksi annetaan "q", ei kysytä ikää ollenkaan, vaan lopetetaan heti.
        String nimi = lueString("Anna nimi: ");
        if (nimi.equals("q")) {
            return null;
        }

        int ika = lueInt("Ja sitten ikäsi: ");

        // Luodaan uusi henkilö äsken kysytyillä tiedoilla ja palautetaan se.
        return new Henkilo(nimi, ika);
    }

    private String lueString(String viesti) {
        // Näytetään viesti, ja palautetaan käyttäjän vastaus siihen.
        System.out.print(viesti);
        return lukija.nextLine();
    }

    private int lueInt(String viesti) {
        String syote = lueString(viesti);

        // Jos syote ei ole numero, luvuksi jää -1.
        int luku = -1;

        try {
            luku = Integer.parseInt(syote);
        } catch (NumberFormatException e) {
            // Ei päästetä ohjelmaa kaatumaan virheellisen syötteen takia.
        }

        return luku;
    }

    public static void main(String[] args) {
        // Luodaan nykyisestä luokasta ilmentymä ja kutsutaan sen kaynnista-metodia.
        new KayttoLiittyma().kaynnista();
    }
}

Staattiseen main-metodiin suositellaan laitettavaksi mahdollisimman vähän koodia, jotta säilytetään hyvä oliomalli. Kun kaikki koodi oli ko. metodin sisällä, luokka meni kokonaan uusiksi. Lisäksi käyttöliittymää ei pidä käynnistää heti rakentajassa, joten siihen pitää kehittää oma käynnistus-metodi.

Tämän pidempiä selostuksia en taida tällä kertaa kirjoitella. Koodi on enimmäkseen selvää ja pari kommenttiakin löytyy, eli sitä pitäisi pystyä lukemaan aika hyvin.

Jos tulee joku ongelmallinen kohta, niin kysy.

Edit. Yritä sitten myös lukea koodia kopioimisen lisäksi, molemmista taidosta tulee olemaan vielä hyötyä paljonkin. ;)

stt-73 [11.06.2012 20:52:05]

#

Hei
Tässä kohdassa herjaa vielä lueHenkiloRekisteriin tekstiä, kaikki 3 kohtaa on alleviivattu 1 ja 3 kehottaa vaihtamaan nimeä ja 2 kehottaa vaihtamaan private void :in muotoon private boolean.

private void lueHenkiloRekisteriin() {
		while (lueHenkiloRekisteriin()) {
		}
	}

	private boolean lueHenkiloRekisteriin() {
		Henkilo henkilo = lueHenkilo();
		//henkilö on null jos nimi on q
		if (henkilo!= null) {
			// jos lukeminen onnistui lisätään henkilo
			rekisteri.lisaa(henkilo);
		}
		// kerrotaan onnistuiko
		return henkilo != null;
	}

Grez [11.06.2012 20:59:08]

#

No mitä jos vaihdat ensimmäisen funktion nimeksi vaikka lueHenkilotRekisteriin? Tuossahan yrität määritellä type signatureltaan täsmälleen saman funktion kahteen kertaan, joka ei tietenkään onnistu. Molemmilla on vieläpä erityyppinen paluuarvo.

stt-73 [11.06.2012 21:16:43]

#

Joo kiitos se oli mun moka, luin ohjeet väärin


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta