INF1000 Mer om objekter Marit Nybakken marnybak@ifi.uio.no 8. mars 2004 Dette dokumentet skal tas med en bøtte salt og forfatter sier fra seg alt ansvar. Dere bør ikke bruke definisjonene i dette dokumentet på eksamen, de er bare ment som forklaringer, ikke strenge definisjoner. Klasser og objekter, repetisjon En klasse er en samling med variable og metoder (som forhåpentligvis er noenlunde relaterte). class Eple { 1
private String farge; private int alder; private int antallmark; void bli spist() { // Gnafs. // Antall mark avgjør her hvor mye // protein konsumenten får i seg. 10 int selg() { // 10 år gammelt eple? // Ikke forvent deg store inntekter return en eller annen passende pris; // Get/Set-metoder 20 Her har vi en klasse som typisk vil være en oppskrift for å lage flere objekter. Den utgjør en definisjon av hva et eple er (i den sammenhengen vi ønsker å bruke eplene, f.eks. i en eplebutikk) og gjør det mulig for oss å lage mange epler til bruk i programmet vårt, ved å følge oppskriften for hvordan et eple skal se ut. Så det gjør vi Eple e = new Eple(); e.setfarge("rød"); e.setalder(3); e.setantallmark(1); Eple e2 = new Eple(); e.setfarge("grønn"); e.setalder(10); e.setantallmark(4); 10 Vi har bare en klasse, men vi kan fremdeles få flere epler i programmet. Klassen gir oss muligheten til å lage flere epler, med og uten mark, og med ulik farge og alder. Et annet eksempel på slike "data-klasser", som inneholder informasjonen om en gjenstand, er klassen String i java-apiet. Den brukes for å lage alle de String-objektene vi trenger i programmet vårt. 2
Vi har også ofte klasser som ikke er rene dataklasser i programmene våre, altså de som skal representere personer, geiter, epler og gravemaskiner. Det kan være klasser som skal utføre bestemte oppgaver, f.eks. filtrere og spille av lyd, eller klasser som skal representere grensesnitt mot brukerne. Vi lager dog som oftest objekter av disse klassene når vi skal bruke dem, selv om vi bare skal ha ett objekt. Konstruktører Når vi lager oss et nytt objekt, så er det ofte som en liten baby. Det vet ingenting og det kan ingenting og det er vanskelig å se forskjell, de er små og rosa og rynkete alle sammen. Vi må få babyen til å vokse opp ved å gi objektvariablene verdier og ellers gjøre klar objektet for bruk etter vi har laget det. Et Person-objekt må få navn og adresse og fødselsnummer, et objekt som skal spille av lydfiler må initialisere lydkortet og kanskje sette opp en lydbuffer. Dette må vi møysommelig gjøre ved å sette objektvariablene og kanskje kalle diverse metoder i objektet etter en etter at vi har opprettet det. Eller, det må vi ikke. Konstruktøren er en spesiell metode som kjøres i det vi oppretter et objekt. Vi kan bruke konstruktøren til hva vi vil, det vanlige er å initialisere objektvariablene (gi dem fornuftige startverdier) og gjøre andre ting for å gjøre objektet "klart til bruk". Konstruktøren kan ta parametre, normalt initialverdier til objektvariablene eller andre ting som sier hvordan dette ob- 3
jektet skal se ut og fungere. class Eple { // Egenskapene til et eple // (i denne sammenhengen) private String farge; private int alder; private int antallmark; // Konstruktør Eple(String f, int a, int m) { 10 // Sett objektvariablene farge = f; alder = a; antallmark = m; // Enda en konstruktør Eple() { // Gi objektvariablene // fornuftige startverdier 20 farge = "ukjent"; alder = 0; antallmark = 0; // Oppgaver eplene kan utføre void bli spist() { // Gnafs. 30 // Antall mark avgjør her hvor mye // protein konsumenten får i seg. int selg() { // 10 år gammelt eple? // Ikke forvent deg store inntekter return en eller annen passende pris; 40 // Get/Set-metoder 4
Eple har nå fått to konstruktører, og altså da to måter å starte opp objektet på. Vi skiller mellom konstruktørene på hvor mange og hvilke parametre de tar. Den første konstruktøren tar tre parametre, f, a og m. Det den deretter gjør er å sette objektets objektvariable lik disse parametrene. Den andre konstruktøren gir også objektvariablene verdier, men den tar ikke inn parametre som sier hva de skal settes til, den gir dem bare defaultverdier. Vel og bra med oppstartsmetoder, men hvordan kaller vi disse metodene? De kalles automatisk i det vi oppretter objektet, altså i det vi sier new Klassenavn(argumenter); Setter vi inn argumenter mellom de to parentesene, vil konstruktøren med parametre som matcher argumentene kalles. Gir vi et heltall som argument, kalles konstruktøren som tar et heltall som parameter (hvis en slik finnes), gir vi to Stringer, kalles konstruktøren som tar to Stringer, osv, akkurat som et vanlig metodekall. Dette betyr at når vi oppretter nye Eple-objekter, kan vi derfor i samme slengen si hvordan eplet skal være, ved å sende argumenter til den første konstruktøren. Eple e4 = new Eple("rosa", 4, 0); De tre verdiene, "rosa", 4 og 0 sendes til konstruktøren, og fanges opp igjen i parameterne f, a og m. Konstruktøren setter deretter objektvariablene lik verdiene som ble tilsendt med farge = f; alder = a; antallmark = m; Og vi får dermed et fiks ferdig rosa 4 dager gammelt eple uten mark på en eneste linje. Her er flere eksempler Eple [ ] epler = new Eple[100]; epler[0] = new Eple("grønn", 2, 3); epler[1] = new Eple("rød", 3, 0); 5
epler[2] = new Eple("brun", 14, 30); //... 10 epler[99] = new Eple("fiolett", 213, 100); // Start opp eplebutikken og selg ut eplene før de løper ut døra av seg selv public, private, protected Public, private og protected bruker vi for å endre hvem som skal ha tilgang til metodene og variablene i en klasse. Når vi sier tilgang, mener vi at vi kan få tak i metoden eller variabelen med dot-notasjon, enten med Klassenavn.variabelnavn, Klassenavn.metodenavn(), objektpeker.variabelnavn eller objektpeker.metodenavn(). public sier at absolutt alle, i andre klasser eller utenfor pakken, skal få tilgang til variabelen eller metoden. Public er en veldig sosial modifikator. I dette kurset har vi ikke lært å lage våre egne pakker, men vi har brukt ferdige pakker som easyio og java.util. Metodene vi bruker i f.eks. In-klassen, som intext og inchar er deklarert som public, slik at vi, som er utenfor pakken, skal kunne bruke dem. private sier at det er bare klassen selv som har tilgang til variabelen eller metoden. Det er som oftest lurt å deklarere alle objektvariable i klassene vi lager som private, da det bare er objektet selv som skal få operere direkte på dem. Alle andre må kalle på metoder i objektet for å få utført operasjoner på dataene i objektet. Slik kan vi sikre, hvis vi skriver fornuftige metoder, at andre ikke prøver å tillegge dataene våre ugyldige verdier. Også metoder kan være deklarert som private. Hvis vi har hjelpemetoder i klassen som det ikke er meningen at andre skal bruke, f.eks. fordi man kan ødelegge data ved å sende med feil parametre, så kan det være lurt å sette dem til private. Dette gir også et mer forståelig grensesnitt mot objektet som sier akkurat hva man kan gjøre med objektet. 6
protected sier at alle subklasser (og i og for seg alle klasser i samme filen) kan få tak i metodene og variablene. Subklasser er ikke dekket i inf1000. ingen modifier fungerer som public, men klasser utenfor pakken kan ikke få tilgang til variabelen eller metoden. Public betyr alle, private bare meg, ingenting betyr nesten alle, inkludert meg selv og deg get/set, finn/hent Er variable satt til private og vi trenger å hente dem ut eller endre på dem, må vi kalle metoder i objektet for å få gjort dette. Metoder som kun skal gi ut verdien til en objektvariabel kalles get-metoder. De er på formen variabelens_type getvariabelnavnet { return variabelnavnet; Metoder som kun skal gi en objektvariabel en ny verdi kalles set-metoder. De er på formen void setvariabelnavnet(variabelens_type variabelnavnet) { this.variabelnavnet = variabelnavnet; Vi skal kun lage slike metoder hvis omverdenen faktisk har bruk for dem. Her er eksempler fra en klasse for bakverkoppskrifter: class Bakverk { private String ingredienser; private String oppskrift; private boolean erkake; Bakverk(String ingredienser, String oppskrift, boolean erkake) { this.ingredienser = ingredienser; this.oppskrift = oppskrift; this.erkake = erkake; 10 7
// get / set // String getingredienser() { return ingredienser; void setingredienser(string ingredienser) { this.ingredienser = ingredienser; 20 boolean geterkake() { return erkake; void seterkake(boolean erkake) { this.erkake = erkake; 30 Pass på så ikke pølsemannen blir sur, bruk private og get/set, så er du lur 8