Forklaring til programmet AbstraktKontoTest.java med tilhørende filer Konto.java, KredittKonto.java, SpareKonto.java

Like dokumenter
public class NaivRiking { private HeldigSnylter minsnylter; public NaivRiking(HeldigSnylter h) { minsnylter = h;

class Book { String title; } class Dictionary extends Book { int wordcount; } class CartoonAlbum extends Book { int stripcount; }

Arv. Book book1 = new Book(); book1. title = "Sofies verden" class Book { String title; } class Dictiona ry extends Book {

Konstruktører. Bruk av konstruktører når vi opererer med "enkle" klasser er ganske ukomplisert. Når vi skriver. skjer følgende:

Eksekveringsrekkefølgen (del 1) Oppgave 1. Eksekveringsrekkefølgen (del 2) Kommentar til oppgave 1. } // class Bolighus

INF Seminaroppgaver til uke 3

Oversikt. INF1000 Uke 1 time 2. Repetisjon - Introduksjon. Repetisjon - Program

Kort om meg. INF1000 Uke 2. Oversikt. Repetisjon - Introduksjon

public static <returtype> navn_til_prosedyre(<parameter liste>) { // implementasjon av prosedyren

Kapittel 7: Mer om arv

i=0 Repetisjon: arrayer Forelesning inf Java 4 Repetisjon: nesting av løkker Repetisjon: nesting av løkker 0*0 0*2 0*3 0*1 0*4

Forelesning inf Java 4

OPPGAVE 1 OBLIGATORISKE OPPGAVER (OBLIG 1) (1) Uten å selv implementere og kjøre koden under, hva skriver koden ut til konsollen?

Løsningsforslag ukeoppg. 6: 28. sep - 4. okt (INF Høst 2011)

Dagens tema Kapittel 8: Objekter og klasser

Repitisjonskurs. Arv, Subklasser og Grensesnitt

Programmering i C++ Løsningsforslag Eksamen høsten 2005

Hva er verdien til variabelen j etter at følgende kode er utført? int i, j; i = 5; j = 10; while ( i < j ) { i = i + 2; j = j - 1; }

TDT4100 Objektorientert programmering

Programmeringsspråket C

public static <returtype> navn_til_prosedyre(<parameter liste>) { // implementasjon av prosedyren

INF1010 våren 2008 Uke 4, 22. januar Arv og subklasser

Eks 1: Binærtre Binærtretraversering Eks 2: Binærtre og stakk

Gjøre noe i hele treet = kalle på samme metode i alle objekten. Java datastruktur Klassestruktur

Læringsmål for forelesningen

LITT OM OPPLEGGET. INF1000 EKSTRATILBUD Stoff fra uke September 2012 Siri Moe Jensen EKSEMPLER

Post-it spørsmål fra timen (Arv og subklasser)

Kapittel 8: Programutvikling

INF1010 Sortering. Marit Nybakken 1. mars 2004

UNIVERSITETET I OSLO

2 Om statiske variable/konstanter og statiske metoder.

Oversikt. INF1000 Uke 2. Repetisjon - Program. Repetisjon - Introduksjon

Hvis en person har inntekt < , så betaler han 10% skatt på alt, og ellers betaler han 10% skatt på de første og 30% på resten.

Hvis en person har inntekt < , så betaler han 10% skatt på alt, og ellers betaler han 10% skatt på de første og 30% på resten.

IN1010. Fra Python til Java. En introduksjon til programmeringsspråkenes verden Dag Langmyhr

Fra Python til Java. En introduksjon til programmeringsspråkenes verden. Dag Langmyhr

Det finnes ingenting. som kan gjøres med interface. men som ikke kan gjøres uten

Løsningsforslag til Eksamen i fag SIF8005 Programmering. Torsdag 10. mai 2001 kl

INF1000 : Forelesning 4

Dagens tema. Hva er kompilering? Anta at vi lager dette lille programmet doble.rusc (kalt kildekoden): Hva er kompilering?

OO-eksempel. Modellen ser slik ut: Studenter + antstudenter : int = 0

i=0 i=1 Repetisjon: nesting av løkker INF1000 : Forelesning 4 Repetisjon: nesting av løkker Repetisjon: nesting av løkker j=0 j=1 j=2 j=3 j=4

UNIVERSITETET I OSLO

INF 1000 høsten 2011 Uke september

INF1000 undervisningen INF 1000 høsten 2011 Uke september

Kompilering Statiske Syntaksanalyse Feilsjekking Eksempel Oppsummering

INF1010 våren Arv og subklasser del 1

Diverse eksamensgaver

INF1010 våren Arv og subklasser del 1

INF1010 Arv. Marit Nybakken 2. februar 2004

IN1010. Fra Python til Java. En introduksjon til programmeringsspråkenes verden Dag Langmyhr

INF 1010, vår 2005 Løsningsforslag uke 11

UNIVERSITETET I OSLO

OBJEKTER SOM EN PROGRAMMERINGS-TEKNIKK

UNIVERSITETET I OSLO

UNIVERSITETET I OSLO

Obligatorisk Innlevering 2

INF1000 Metoder. Marit Nybakken 16. februar 2004

Introduksjon til objektorientert. programmering. Hva skjedde ~1967? Lokale (og globale) helter. Grunnkurs i objektorientert.

INF1000: Forelesning 6. Klasser og objekter del 1

INF1000: Forelesning 7

Beskrivelse av programmeringsspråket Compila15 INF Kompilatorteknikk Våren 2015

programeksempel Et større En større problemstilling Plan for forelesingen Problemstillingen (en tekstfil) inneholdt ordet "TGA"

Tre måter å lese fra terminal. Java 4. Eksempel. Formatert utskrift til skjerm

Algoritmer og datastrukturer Kapittel 3 - Delkapittel 3.1

Introduksjon til objektorientert programmering

INF1000: noen avsluttende ord

Jentetreff INF1000 Debugging i Java

Feilmeldinger, brukerinput og kontrollflyt

Med Svarforslag UNIVERSITETET I OSLO. Det matematisk-naturvitenskapelige fakultet. 3 sider (side 6, 7 og 8, rives ut, fylles ut og leveres)

Oppsummering. Kort gjennomgang av klasser etc ved å løse halvparten av eksamen Klasser. Datastrukturer. Interface Subklasser Klasseparametre

Forelesning inf Java 5

INF Notater. Veronika Heimsbakk 10. juni 2012

Forelesning inf Java 5

INF Obligatorisk innlevering 5

IN1010 V19, Obligatorisk oppgave 2

La oss begynne med en repetisjon av hva som skjer når du kjører Javaprogrammet

UNIVERSITETET I OSLO

Dagens tema: 12 gode råd for en kompilatorskriver

INF Uke 10. Ukesoppgaver oktober 2012

EKSAMENSFORSIDE Skriftlig eksamen med tilsyn

TOD063 Datastrukturer og algoritmer

INF1000: Forelesning 7. Konstruktører Static

En algoritme for permutasjonsgenerering

IN1010 våren 2018 Tirsdag 15. mai. Repetisjon av subklasser og tråder. Stein Gjessing Institutt for informatikk Universitetet i Oslo

UNIVERSITETET I OSLO

Forkurs INF1010. Dag 1. Andreas Færøvig Olsen Tuva Kristine Thoresen

Verden - Del 2. Steg 0: Oppsummering fra introduksjonsoppgaven. Intro

Eivind Gard Lund. 24. Mars 2009 Foilene bygger på 2009 utgaven av Andreas Svendsen

INF Repetisjon: Hvordan bygge treet og analysere? 8. september Typisk situasjon. De problematiske syntaks-diagrammene

Tråder Repetisjon. 9. og 13. mai Tråder

En klasse som arver, eller selv deklarerer en abstrakt metode, må deklareres som abstrakt.

EKSAMEN 6108/6108N PROGRAMMERING I JAVA Alt trykt og skriftlig materiale.

Utførelse av programmer, metoder og synlighet av variabler i JSP

INF1000 EKSTRATILBUD. Stoff fra uke 1-5 (6) 3. oktober 2012 Siri Moe Jensen

Løse reelle problemer

UNIVERSITETET I OSLO

INF1000 (Uke 15) Eksamen V 04

INF1000 (Uke 15) Eksamen V 04

Transkript:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 Forklaring til programmet AbstraktKontoTest.java med tilhørende filer Konto.java, KredittKonto.java, SpareKonto.java Dette programmet gjør ikke noe nyttig, poenget er bare å demonstrere bruk av en abstrakt klasse og hvordan overlagring av metoder fungerer (jfr kap 7.7-8 i boka). Ved å kopiere disse filene til egen bruker og kompilere dem, vil man kunne se hvordan programmet virker og eventuelt prøve ut forskjellige endringer for å forstå det enda bedre. Bruk da koden på de ovennevnte filene direkte, ikke denne her som er tilsølt med linjenumre... public class AbstraktKontoTest { public static void main(string[] args) { //Konto k = new Konto(); //vil ikke kompilere fordi //Konto-klassen nå er abstrakt. Nysgjerrige kan fjerne //kommentering og se hva som skjer Konto k1 = new KredittKonto(2000,9000); Konto k2 = new SpareKonto(33000); if (k1.uttak(6000)) { System.out.println("k1 har " + k1.hentsaldo()); if (!k2.uttak(222333)) { System.out.println("k2 nektet uttak av 222333"); if (k2.uttak(11000)) { System.out.println("k2 har " + k2.hentsaldo()); if (!k2.uttak(25)) { System.out.println("k2 nektet uttak av 25"); public abstract class Konto implements Skattbar { protected int saldo; // public Konto() { //oppretter konto med saldo 0 saldo = 0; public int hentsaldo() { return saldo; public int likningsverdi() { return this.hentsaldo(); public void innskudd(int belop) { //setter inn beløpet på kontoen saldo += belop; //samme som saldo=saldo+belop; public abstract boolean uttak(int belop); //alle kontotyper må ha en uttaksmetode, men de kan

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 //implementere den på ulike måter public class KredittKonto extends Konto { private int kredittgrense; public KredittKonto(int belop, int grense) { //konto med gitt saldo og kredittgrense saldo = belop; kredittgrense = grense; public boolean uttak(int belop) { if (saldo + kredittgrense >= belop) { saldo -= belop; return true; else { return false; public class SpareKonto extends Konto { private static int maxantuttakperaar = 1; private int antuttakiaar; public SpareKonto(int belop) { saldo = belop; antuttakiaar = 0; public int hentantuttak() { return antuttakiaar; public boolean uttak(int belop) { if (antuttakiaar < maxantuttakperaar) { if (saldo >= belop) { saldo -= belop; antuttakiaar++; return true; return false;

Når man starter programmet med kommandoen java AbstraktKontoTest vil utførelsen begynne med main()-metoden (linje 11). Linje 12-14 er kommentarer som ikke blir kjørt. Hvis man tar vekk kommentaren for linje 12 og kompilerer på nytt, vil man se at man får en feilmelding, dette fordi klassen Konto nå er deklarert som abstract (jfr linje 32), som betyr at det ikke lenger er mulig å instansiere objekter av denne klassen (dvs new Konto() er ulovlig) Venstre side av de to tilordningene i linje 15-16 viser at det likevel er helt greit å bruke objektreferanser av typen Konto. På høyre side, der instansene opprettes med new, er det imidlertid et KredittKonto-objekt og et SpareKonto-objekt som lages, ved kall til konstruktorene (linje 55-59, 73-76). Disse to konstruktorene kjøres altså som følge av dette, resultatet blir at vi får opprettet en kredittkonto med saldo 2000 og kredittgrense 9000 og en sparekonto med saldo 33000 og antuttakiaar 0 (linje 75). Vi har da følgende situasjon: k1 En Kontoreferanse saldo kredittgrense 2000 9000 En KredittKontoinstans k2 En Kontoreferanse antutt. saldo 33000 0 En SpareKontoinstans 1 maxantuttakperaar Statisk variabel i SpareKonto-klassen Denne er plassert utenfor objektet fordi den er statisk og dermed bare vil finnes i ett eksemplar for hele klassen, dvs det vil bare finnes en maxantuttakperaar-variabel i programmet selv om vi skulle opprette diverse ulike SpareKontoobjekter med new SpareKonto(...) I denne figuren ser vi altså at referansene vi bruker (som deklareres på venstre side av linje 15 og 16) ikke er av samme type som selve objektene som referansene viser til. Dette er imidlertid helt greit, kravet er at et objekt som tilordnes en referanse må være av en type som er kompatibel med referansen. Mer spesifikt betyr dette: Objektet må ha samme type som referansen, eller Objektet må tilhøre en direkte eller indirekte subklasse av referansens klasse (indirekte betyr her subklassing gjennom flere ledd, f.eks. at den er en subklasse av en subklasse) Eller (som vil bli vist i det andre eksemplet på denne katalogen, NaivRiking.java) referansens type må være et grensesnitt som objektets klasse implementerer Hvis typeforskjellen derimot hadde gått motsatt veg, at vi f.eks hadde brukt en KredittKontoreferanse men satt inn et Konto-objekt (som riktignok uansett er umulig her fordi Konto er

abstrakt, men hvis vi nå tenker oss at den som i tidligere eksempler var konkret...), ville dette vært uakseptabelt. Poenget er at siden KredittKonto er en mer spesifikk klasse enn Konto, kan det finnes metoder som en KredittKonto tilbyr (f.eks endrekredittgrense()) som en Konto ikke tilbyr. Det motsatte vil derimot ikke kunne være tilfelle pga arv vil en KredittKonto alltid tilby til omverdenen alle metoder som en Konto tilbyr. Det er derfor trygt å tilordne en KredittKonto-instans til en Konto-referanse, da KredittKonto-instansen vil være i stand til å svare på alle metodekall som kan bli gitt mot denne Konto-referansen. Resten av programmet viser nå poenget med overlagring. I linje 17 er betingelsen for ifsetningen (k1.uttak(6000)). Dette er en gangbar betingelse for en if-setning fordi metoden returnerer boolean, jfr linje 48, 60, 80. Men hvilken av disse er det som blir kalt her? Siden k1 har type Konto, kunne man tro at det er Konto sin uttak()-metode, linje 48. Men denne er deklarert som abstrakt, har dermed ingen kropp med kodelinjer og kan således ikke utføres. Men selv om denne hadde vært en vanlig metode med kodelinjer for utførelsen, ville det ikke ha vært denne som ble utført for dette kallet. Det som skjer i denne situasjonen er nemlig at Det er blitt rettet et kall uttak() mot objekt-instansen som referansen k1 viser til. Kjøretidssystemet vil da undersøke hva slags objekt k1 viser til, og finner at dette er et KredittKonto-objekt (jfr figuren over). Kjøretidssystemet vil dermed sørge for at det er uttak()-metoden i klassen KredittKonto som blir utført i dette tilfellet, med de konkrete dataene som det aktuelle kredittkonto-objektet inneholder (dvs en saldo på 2000 og en kredittgrense på 9000). Altså er det metoden på linje 60 som blir utført her, og argumentet int belop får verdien 6000 som gitt inn i kallet linje 17. Saldo + kredittgrense er 11000, som er større enn 6000, uttaket vil dermed bli utført, dvs saldoen reduseres til 4000 og metoden returnerer true. Dermed blir betingelsen linje 17 true og utskriftssetningen linje 18 blir utført. k1.hentsaldo() vil på samme måte som k1.uttak() føre til et kall av hentsaldo-metoden til KredittKonto-objektet. Som man ser, står det ingen slik metode i klassen KredittKonto, men den fins tilgjengelig der likevel fordi den er arvet fra klassen Konto, jfr linje 38-40. Denne returnerer simpelthen saldoen, så utskriften blir k1 har 4000. De tre neste if-setningene (linje 20, 23, 26) vil virke tilsvarende. Her er referansen imidlertid k2, som er en sparekonto. Da vil det være uttak()-metoden i SpareKonto-klassen som blir utført (linje 80-89). I linje 20 prøver man å ta ut 222333. Testen i linje 81 går bra fordi antuttakiaar er 0 mens maxantuttakperaar er 1. Men testen i linje 82 slår feil fordi beløpet som forsøkes tatt ut er altfor stort for saldoen, hvilket betyr at linje 83-85 ikke utføres, og i stedet returnerer man false (linje 88). Merk imidlertid at det nå står! (som betyr ikke) fremst i betingelsen linje 20. Dette snur opp ned på den logiske verdien, så når uttak()-kallet returnerer false, blir betingelsen som helhet true. Dermed blir utskriftssetninga linje 21 utført. Uttaket i linje 23 er saldoen stor nok til å dekke. Siden det hittil ikke har skjedd noen endringer i sparekonto-objektet vil testen i linje 81 gå bra som sist, og nå går også testen i linje 82 bra. Da utføres linje 83, som reduserer saldoen fra 33000 til 22000, linje 84 som øker antuttakiaar fra 0 til 1, og 85 som returnerer true. Når man utfører en return i en metode, stoppes videre utførelse av metodekoden (så man kommer aldri til linje 88 hvis man utfører linje 85). Man kommer dermed tilbake til linje 23 med true, som gjør at if-testen her går bra og utskriften linje 24 utføres. For uttaket i linje 26 er det derimot testen i linje 81 som skjærer seg, fordi antuttakiaar for dette objektet ble øket til 1 i det forrige uttaket. Dermed utføres ikke linjene 82-87, og man

kommer til linje 88 og returnerer false. Nå står det imidlertid igjen! fremst i betingelsen linje 26, så betingelsen som helhet blir true og man utfører utskriften linje 27. For å oppsummere det vi har sett av dette eksemplet: Det er ikke typen til objektreferansen som avgjør hvilken metode som blir utført i situasjoner hvor man har overlagring (dvs. flere metoder med samme navn og argumentliste), men typen til selve objektet som referansen viser til. Kjøretidssystemet vil finne ut av dette under kjøring av programmet og velge riktig metode. Typen til objektreferansen behøver ikke være identisk med typen til selve objektet. Dette er fordi referansevariabelen (f.eks Konto k1, Konto k2) ikke inneholder objektet, men bare adressen til objektet. En referanse til et KredittKonto-objekt krever derfor ikke større plass enn en referanse til et Konto-objekt, selv om KredittKonto-objektet arver alle variable fra Konto-objektet og dessuten legger til en ekstra variabel kredittgrense (akkurat som et stort hus ikke behøver ha en lenger postadresse enn et lite hus, eller en feit mann ikke trenger flere siffer i telefonnummeret sitt enn en tynn mann) På tampen kan man spørre hvorfor man overhodet behøvde den abstrakte metoden uttak() i klassen Konto (linje 48) når denne aldri ble kalt og uansett ville vært umulig å utføre fordi den ikke har noen kodelinjer inni seg (og dermed var det heller ikke noe subklassene kunne dra fordel av å arve mhp denne metoden). Når kjøretidssystemet likevel kunne finne ut automatisk hvilket objekt det faktisk ble referert til, og kalle rett metode, skulle man tro at det var nok at metoden uttak() fantes i KredittKonto og SpareKonto. Svaret på dette er den prosessen som går forut for kjøring, nemlig kompileringen. Et strategisk valg ifbm definisjonen av Java var at man ville ha statisk typesjekking (som man også har i C++, Pascal og mange andre vanlige programmeringsspråk), dvs. at kompilatoren skal kunne sjekke om typer stemmer overens ifbm tilordninger, metodekall etc. Hvis vi ikke hadde deklarert noen metode uttak() i superklassen Konto (og den som vil prøve kan raskt oppnå dette ved å sette kommentartegn fremst i linje 48 og kompilere filene Konto.java og AbstraktKontoTest.java på nytt), ville følgende ha skjedd: Kompilatoren kompilerer greit nedover inntil den kommer til kallet k1.uttak(...) Den vet på dette tidspunktet at k1 er av typen Konto, og sjekker om Konto har noen uttak()-metode. Konto har ingen uttak()-metode, og kompilatoren avbryter sitt arbeid med en feilmelding. Altså: mens kjøretidssystemet er i stand til å kalle riktig metode fordi det ganske enkelt kan sjekke hvilke objekter referansene faktisk viser til, er dette umulig for kompilatoren fordi det foreløpig ikke er opprettet noen objekter (kompilatoren kjører jo ikke programmet men sjekker bare at det er grammatisk korrekt). I dette enkle eksemplet kunne man likevel tenke seg at man uten altfor store anstrengelser kunne ha greid å lage en kompilator som så at kallet var ok, fordi vi like ovenfor hadde tilordnet k1 til et KredittKonto-objekt. Generelt ville en slik avansert sjekk imidlertid være umulig, da new-kallene f.eks kan skje i innfløkte ifsetninger hvor betingelsene bl.a. kan være basert på brukerens input, som det jo er umulig for kompilatoren å vite noe om. Det er derfor mest hensiktsmessig at kompilatoren ikke gjør noe forsøk på å analysere hvordan programmet ville kjøre (som i ytterste instans ville kreve at man kjørte det, som jo ikke er kompilatoren men kjøretidssystemets jobb), men nøyer seg med en rent formell grammatikksjekk. I så måte stemmer ikke kallet k1.uttak() hvis ikke Konto-klassen har noen slik metode. Men straks den har en uttak()-metode, om så bare abstrakt, vil kallet være ok, fordi alle subklasser av Konto som man skal kunne lage instanser av med dette tvinges til å ha en uttak()-metode som kan utføres.