INF1010 våren 2012 Torsdag 16. februar Arv, subklasser og grensesnitt - del 2 Stein Gjessing Institutt for informatikk
Dagens tema nummer 1 Norsk: Grensesnitt Engelsk: Interface 2
Hva er objektorientert programmering? En sort boks public void settinn(int tall) public int taut( ) Ukjent implementasjon av metode Ukjent implementasjon av metode Ukjente private data Dette kaller vi metodens signatur En tankemodell Metoden settinn gjør at objektet tar vare på tallet som er parameter til metode. Metoden taut sletter fra objektet et av de tallene som tidligere er satt inn. Metoden returnerer det tallet som er slettet. Er dette god nok beskrivelse?! 3
public void settinn(int tall) public int taut( ) En tankemodell et skall Inni er det ingen ting Et interface! er en! beskrivelse! av en! sort boks! interface Heltallsbeholder { public void settinn(int tall); public int taut( ); 4
Med Javadoc /** Objektene av alle klassene som implementerer dette gresesnittet tar vare * på heltall. * * @author Stein Gjessing * versjon 10. februar 2012 */ interface Heltallsbeholder { /** * Gjør at objektet tar vare på tallet som er parameter til metoden * *@param tall tallet som objektet skal ta vare på */ public void settinn(int tall); /** * Sletter fra objektet et av de tallene som tidligere er satt inn. * Metoden returnerer det tallet som er slettet. * *@return tallet som er slettet */ public int taut( ); 5
6
interface Heltallsbeholder { public void settinn(int tall); public int taut( ); public void settinn(int tall) public int taut( ) En tankemodell Så må vi fylle innhold i den sorte boksen: class XXHeltallsbehldr implements Heltallsbeholder { < Java datastruktur > public void settinn(int tall) { < Java kode > public int taut( ) { < Java kode > public void settinn(int tall) public int taut( ) kode kode datastruktur Objekt av klassen XXHeltallsbehldr 7
interface Heltallsbeholder { public void settinn(int tall); public int taut( ); public void settinn(int tall) public int taut( ) En tankemodell class EnkelHeltlbehldr implements Heltallsbeholder { private int tallet = -1; public void settinn(int tall) { tallet = tall; public int taut( ) { int temp= tallet; tallet = -1; return temp; FULLSTENDIG PROGRAM Type: EnkelHeltlbehldr Navn: beholder public void settinn(int tall) public int taut( ) class BrukBeholder { public static void main (String[] argumenter) { EnkelHeltlbehldr beholder = new EnkelHeltlbehldr(); beholder.settinn(17); System.out.println(beholder.taUt()); tallet Objekt av klassen EnkelHeltlbehldr 8
interface Heltallsbeholder { public void settinn(int tall); public int taut( ); public void settinn(int tall) public int taut( ) En tankemodell class EnkelHeltlbehldr implements Heltallsbeholder { private int tallet = -1; public void settinn(int tall) { tallet = tall; public int taut( ) { int temp= tallet; tallet = -1; return temp; FULLSTENDIG PROGRAM Type: Heltallsbeholder Navn: beholder public void settinn(int tall) public int taut( ) class BrukBeholder { public static void main (String[] argumenter) { Heltallsbeholder beholder = new EnkelHeltlbehldr(); beholder.settinn(17); System.out.println(beholder.taUt()); tallet Objekt av klassen EnkelHeltlbehldr 9
interface Heltallsbeholder { public void settinn(int tall); public int taut( ); Metoden settinn gjør at objektet tar vare på tallet som er parameter til metoden. Metoden taut sletter fra objektet et av de tallene som tidligere er satt inn. Metoden returnerer det tallet som er slettet. class EnkelHeltlbehldr implements Heltallsbeholder { private int tallet = -1; public void settinn(int tall) { tallet = tall; public int taut( ) { int temp= tallet; tallet = -1; return temp; Kompilatoren sjekker at implementasjonen har de riktige metodene med de riktige parameterene! MEN:! Bare du, som er et menneske, kan sjekke at implementasjonen overholder de SEMANTISKE KRAVENE! til metodene.! 10
Institutt for informatikk har 14 forskningsgrupper. En av disse heter Presis Modellering og Analyse (PMA). Her arbeider de bl.a. med å formalisere disse sematiske kravene, slik at du kan få hjelp av datamaskinen til å sjekke at implementasjonen overholder de sematiske kravene. Mer på en senere forelesning. Kompilatoren sjekker at implementasjonen har de riktige metodene med de riktige parameterene! MEN:! Bare du, som er et menneske, kan sjekke at implementasjonen overholder de SEMANTISKE KRAVENE! til metodene.! public void settinn(int tall) public int taut( ) De sematiske kravene kalles også en kontrakt (mellom brukeren av objektet og objektet)(mer senere) 11
Klassehierarki Heltallsbeholder EnkelHeltlbehldr Når en klasse implementerer et gresesnitt (interface) tegner vi det nesten på samme måte som en superklasse / subklasse. For å markere at superklassen ikke er det, men et grensesnitt. kan vi enten skrive interface i boksen, eller vi kan gjøre navnet på grensesnittet (og boksen?) kursiv. Norsk: Grensesnitt Engelsk: Interface 12
public void settinn(int tall) public int taut( ) Metoden settinn gjør at objektet tar vare på tallet som er parameter til metoden. Metoden taut sletter fra objektet et av de tallene som tidligere er satt inn. Metoden returnerer det tallet som er slettet. En tankemodel for gresesnittet Heltallsbeholder Et grensesnitt beskriver en rolle som alle objektene som implementerer dette grensesnittet må kunne spille" public void settinn(int tall) public int taut( ) Et objekt av en klasse som implementerer grensesnittet Heltallsbeholder... Men objektene kan gjerne ha andre metoder i tillegge " (kunne spille andre roller også)" (mer om dette neste gang)" 13
Dagens tema nummer 2 Virtuelle metoder som er det samme som Polymorfi 14
Klassehierarki: Personbil Bil Repetisjon: Subklasser! class Bil { <lys beige egenskaper> class Personbil extends Bil { <røde egenskaper> class Lastebil extends Bil { < grønne egenskaper> class Drosje extends Personbil { < gule egenskaper> Alle lastebiler Lastebil Drosje Alle biler Alle drosjer Alle personbiler 46 objekter 46 new 15
Omdefinering av metoder - polymorfi Vi har sett at med subklasser kan vi utvide en eksisterende klasse med nye metoder (og nye variable). En subklasse kan også definere en metode med samme signatur som en metode i superklassen, men med ulikt innhold. Den nye metoden vil omdefinere (erstatte) metoden som er definert i superklassen. Metoder som kan omdefineres på denne måten kalles virtuelle metoder. Alle metodene i et grensesnitt er virtuelle I Java er alle metoder virtuelle, så sant de ikke er deklarert med final. Virtuelle metoder = polymorfi! 16
interface KanBjeffe{ void bjeff(); Polymorfi: eksempel For objekter av typen VanligHund er det denne metoden som gjelder. class VanligHund implements KanBjeffe{ //... public void bjeff() { System.out.println("Vov-vov"); For objekter av typen class Rasehund extends VanligHund { Rasehund er det denne //... metoden som gjelder. public void bjeff() { System.out.println("Voff-voff"); KanBjeffe VanligHund Rasehund
Polymorfi: eksempel VanligHund v Rasehund r VanligHund g KanBjeffe k void bjeff() { System.out.println("Vov-vov"); void bjeff() { System.out.println("Vov-vov"); void bjeff() { System.out.println("Voff-voff"); Anta dette programmet: Hva skrives ut ved hvert av kallene: VanligHund v = new VanligHund(); Rasehund r = new Rasehund(); VanligHund g = r; v.bjeff(); r.bjeff(); g.bjeff(); Vov-vov Voff-voff Voff-voff KanBjeffe k = r; k.bjeff(); Voff-voff 18
Eksempel: class Musikk Oppgave: Hva skrives ut når programmet Musikk.java kjøres? class Musikk { public static void main (String[] args) { Instrument inst = new Piano(); inst.skrivdefinisjon(); class Instrument { void skrivdefinisjon () { System.out.println ("Et instrument er noe man kan spille på"); class Piano extends Instrument { void skrivdefinisjon () { System.out.println ("Et piano er et strengeinstrument"); Instrument Piano 19
Instrument inst void skrivdefinisjon() { System.out.println( Et instr..."); Oppgave: Hva skrives ut når programmet Musikk.java kjøres? void skrivdefinisjon() { System.out.println( Et piano... "); class Musikk { public static void main (String[] args) { Instrument inst = new Piano(); inst.skrivdefinisjon(); class Instrument { void skrivdefinisjon () { System.out.println ("Et instrument er noe man kan spille på"); class Piano extends Instrument { void skrivdefinisjon () { System.out.println ("Et piano er et strengeinstrument"); Instrument Piano 20
Musikk versjon 2 Hva skjer i dette tilfellet? Instrument inst void skrivdefinisjon() { System.out.println( Et instr..."); void skrivdefinisjon(string overskrift) { System.out.println(... ); System.out.println(... ); class Musikk { public static void main (String[] args) { Instrument inst = new Piano(); inst.skrivdefinisjon(); class Instrument { void skrivdefinisjon () { System.out.println("Et instrument er noe man kan spille på"); class Piano extends Instrument { void skrivdefinisjon (String overskrift) { System.out.println(overskrift); System.out.println("Et piano er et strengeinstrument"); 21
Musikk versjon 3 Hva skjer a? class Musikk { public static void main (String[] args) { Instrument inst = new Piano(); inst.skrivdefinisjon(); class Instrument { void skrivdefinisjon(string overskrift) { System.out.println(overskrift); System.out.println("Et instrument er noe man kan spille på"); class Piano extends Instrument { void skrivdefinisjon () { System.out.println("Et piano er et strengeinstrument"); 22
Musikk-eksemplene: Lærdom Når vi ser på et objekt via en superklasse-peker, mister vi vanligvis tilgang til metoder og variable som er definert i subklassen. Dersom en metode i subklassen også er definert (med samme signatur) i superklassen har vi likevel tilgang via superklasse-pekeren, fordi objektets dypeste metode brukes. Slike metoder kalles virtuelle metoder, og denne mekanismen kalles polymorfi. Det som er relevant er derfor hvilke metoder som finnes i superklassen (med hvilke parametre), men ikke nødvendigvis innholdet i metodene. Det siste er (bare) viktig under kjøring. Samme signatur = samme navn og nøyaktig samme parametre (inkl. returtype) 23
Flere virtuelle metoder class Vare { int pris; int prisutenmoms() { return pris; Anta: Vare v = new Vare(); v.pris = 100; int prismedmoms() { return (int) (1.25*prisUtenMoms()); SalgsVare s = new SalgsVare(); s.pris = 100; s.rabatt = 25; class SalgsVare extends Vare { int rabatt; // I prosent... Hva blir nå: int prisutenmoms() { return pris (pris*rabatt/100); v.prismedmoms() s.prismedmoms() 24
pris 100 Anta: Vare v = new Vare(); v.pris = 100; Vare v2 SalgsVare s = new SalgsVare(); s.pris = 100; s.rabatt = 20; Vare v2 = s; SalgsVare s Hva blir nå: v.prismedmoms(); s.prismedmoms(); v2.prismedmoms(); Vare v int prisutenmoms ( ) { return pris; int prismedmoms { return (int) (1.25*prisUtenMoms ( ) ); pris int prisutenmoms ( ) { return pris; int prismedmoms { return (int) (1.25*prisUtenMoms ( ) ); rabatt 100 20 int prisutenmoms ( ) { return pris (pris*rabatt/100); 25
Polymorfi: skrivdata I universitets-eksemplet så vi at klassene Student og Ansatt hadde nesten like skrivdata-metoder: // I klassen Student: void skrivdata() { System.out.println("Navn: " + navn); System.out.println("Telefon: " + tlfnr); System.out.println("Studieprogram: " + program); // I klassen Ansatt: void skrivdata() { System.out.println("Navn: " + navn); System.out.println("Telefon: " + tlfnr); System.out.println("Lønnstrinn: " + lønnstrinn); System.out.println("Timer: " + antalltimer); 26
Nøkkelordet super Nøkkelordet super brukes til å aksessere variable / metoder i objektets superklasse. Dette kan vi bruke til å la superklassen Person ha en generell skrivdata, som så kalles i subklassene: // I klassen Person: void skrivdata() { System.out.println("Navn: " + navn); System.out.println("Telefon: " + tlfnr); // I klassen Student: void skrivdata() { super.skrivdata(); System.out.println("Studieprogram: " + program); // Tilsvarende i klassen Ansatt: void skrivdata() { super.skrivdata(); System.out.println("Lønnstrinn: " + lønnstrinn); System.out.println("Timer: " + antalltimer); 27
class StudentRegister { public static void main(string [] args) { Student stud = new Student(); Person pers = new Person(); stud.skrivdata(); pers.skrivdata(); // Her brukes definisjonen i Student // Her brukes definisjonen i Person Person pers2 = stud; pers2.skrivdata(); // Hvilken definisjon benyttes her? Person pers Student stud Person pers2 void skrivdata() { System.out.println("Navn: " + navn); System.out.println("Telefon: " + tlfnr); void skrivdata() { System.out.println("Navn: " + navn); System.out.println("Telefon: " + tlfnr); Regel: Det er objekttypen, ikke pekertypen, som avgjør hvilken definisjon som gjelder når en metode er virtuell. void skrivdata() { super.skrivdata(); System.out.println("Studprgm: " + prgm); 28
Omdefinering av variable En subklasse kan også omdefinere (skyggelegge) variable som er definert i superklassen. MEN: Dette bør IKKE brukes!!! Sjelden nødvendig Reduserer lesbarheten Kan føre til uventet oppførsel Og mer trenger dere ikke å vite om det 29
Mer om grensesnitt interface KanBjeffe{ void bjeff(); interface Svigermor extends KanBjeffe { boolean okpaabesok(); KanBjeffe Svigermor class NorskSvigermor { boolean hyggelig = false; public void bjeff( ) { System.out.println( Uff uff ); public boolean okpaabesok() { return hyggelig; NorskSvigermor 30
To (eller flere) grensesnitt = to (eller flere) roller KanBjeffe Utkledd Karnevalshund Denne figuren avspeiler interface -ene og class -en på neste siden 31
interface KanBjeffe{ void bjeff(); interface Utkledd { int antallfarger(); class Karnevalshund implements KanBjeffe, Utkledd { private boolean farger; Karnevalshund (int frg) { farger = frg; public void bjeff( ) { System.out.printl( Voff - voff ); public boolean antallfarger() { return farger; 32
Eller bedre: KanBjeffe VanligHund Utkledd Karnevalshund Denne figuren avspeiler interface -ene og class -ene på neste siden 33 33
interface KanBjeffe{ void bjeff(); interface Utkledd { int antallfarger(); class VanligHund implements KanBjeffe { public void bjeff() { System.out.println("Vov-vov"); class Karnevalshund extends VanligHund implements Utkledd { private boolean farger; Karnevallshund (int frg) { farger = frg; public boolean antallfarger() { return farger; 34
Klassehierarki: Et eksempel fra Java-biblioteket ArrayList AttributeListt RoleList RoleUnresolvedList Oppgave: Slå opp på ArrayList i Javabiblioteket og se at de tre klassene er direkte subklasser av ArrayList (direkte naboer i klassehierarkiet) ( Direct Known Subclasses )
Klassehierarki: Også fra Java-biblioteket: Feil-klasser Object Throwable Exception Exception Virtual- Machine- Error IOError RuntimeException IOException Oppgave: Slå opp i Javabiblioteket AWTException... og ca. 60 andre
Tilordning av pekere class LagFrukt { public static void main(string[] args) { Frukt f; Eple e; Appelsin a; e = new Eple(); f = e; a = f; // class Frukt {.. class Eple extends Frukt {.. class Appelsin extends Frukt {.. Eple Frukt Appelsin 37
Hva slags objekt er dette? Den boolske operatoren instanceof hjelper oss å finne ut av hvilken klasse et gitt objekt er, noe som er nyttig i mange tilfeller: class TestFrukt { public static void main(string[] args) { Eple e = new Eple(); skrivut(e); static void skrivut(frukt f) { if (f instanceof Eple) System.out.println("Dette er et eple!"); else if (f instanceof Appelsin) System.out.println("Dette er en appelsin!"); Frukt class Frukt {.. class Eple extends Frukt {.. class Appelsin extends Frukt {.. Eple Appelsin 38
Men: Prøv å unngå instanceof Istedenfor å teste hvilken klasse objektet er av, be objektet gjøre jobben selv: Bedre program: class TestFrukt2 { public static void main(string[] args) { Fruk2 f = new Eple2(); f.skrivut( ); class Frukt2 { void skrivut( ) { class Eple2 extends Frukt2 { void skrivut( ) { System.out.println("Dette er et eple!"); class Appelsin2 extends Frukt2 { void skrivut( ) { System.out.println("Dette er en appelsin!"); 39
god Biler og mer bruk av instanceof HashMap <String, Bil> h; (og litt casting (nytt)) h = new HashMap <String, Bil> ( );... for (Bil b: h.values()) { String nr = b.regnr; // kall på virtuell metode: b.skatt( ); if (b instanceof Pesonbil) { Personbil pb = (Personbil) b; int pas = pb.antpass; else { if (b instanceof Lastebil) { Lastebil ls = (Lastebil) b; double lv = ls.lastevekt; class Bil { String regnr; void skatt(){... class Personbil extends Bil { int antpass; void skatt(){... class Lastebil extends Bil { double lastevekt; void skatt(){... class Drosje extends Personbil { int LøyveNr; void skatt(){... 40
Konvertering av referanser (pekere) Anta at vi har: class Student extends Person { Student stud = new Student(); Ved tilordningen Person pers; pers = stud; har vi en implisitt konvertering fra Student- til Personreferanse. Hvis vi nå ønsker å få tak i de spesielle Studentegenskapene, må vi foreta en eksplisitt konvertering tilbake til Student igjen: Student stud2 = (Student) pers; Dette kalles casting (class-cast) på engelsk, typekonvertering på norsk. Medfører kjøretidstest. 41
Konvertering av referanser (forts.) Hva hvis vi istedenfor hadde hatt: Person pers = new Person(); Student stud = (Student) pers; Dette godkjennes av kompilatoren, men ved kjøring går det galt, og vi får feilmeldingen java.lang.classcastexception (fordi pers ikke peker på et objekt med alle Student egenskapene) For å unngå denne feilen, bør instanceof brukes: if (pers instanceof Student) { Student stud = (Student) pers; (Har det objektet pers peker på alle Student -egenskapene?) 42
Konvertering mellom flere nivåer MasterStudent master = new MasterStudent(); Person Konvertering oppover: Student stud = master; Person pers Person pers = master; Student MasterStudent Konvertering nedover: stud = (Student) pers Student stud master = (MasterStudent) pers (fordi dette krever kontroll under kjøring) MasterStudent master Regel: Alle pekere har lov til å peke bortover og nedover (men ikke oppover ) 43
Oppgave Brev Anta at vi har deklarasjonene class Brev { class Soknad extends Brev { Soknad class Kjaerlighetsbrev extends Brev { Avgjør hvilke av følgende uttrykk som er lovlige: Kjaerli Soknad s1 = new Soknad(); Soknad s2 = new Brev(); Brev b1 = new Soknad(); Brev b2 = (Brev) new Soknad(); Soknad s3 = new Kjaerlighetsbrev(); Soknad s4 = (Soknad) new Kjaerlighetsbrev(); Brev b3 = (Soknad) new Brev(); Lovlig Ulovlig 44
Konvertering til klassen Object Alle klasser i Java er subklasser av klassen Object. Når vi skriver class Person { så tolker Java dette som class Person extends Object { Dermed kan vi alltid konvertere en referanse oppover til klassen Object: Person pers = new Person(); Object obj = pers; For å snakke om egenskapene til Person-objektet må vi konvertere nedover igjen: (Person) obj Object obj Person pers class Object er alle klassers mor 45
Object: tostring og equals Klassen Object inneholder bl.a. tre viktige metoder: String tostring() returnerer en String-representasjon av objektet boolean equals(object o) sjekker om to objekter er like (i Object det samme som pekerlikhet) int hashcode( ) returnerer en hash-verdi av objektet Disse metodene kan man så selv redefinere til å gjøre noe mer fornuftig. Poenget er at en bruker av en klasse vet at disse metodene alltid vil være definert (pga. Polymorfi) 46
Eksempel på tostring og equals class Punkt { int x, y; Punkt(int x0, int y0) { x = x0; y = y0; Anta: class Punkt2 { int x, y; Punkt2(int x0, int y0) { x = x0; y = y0; public String tostring() { return ("x = "+x+" y = "+y); Punkt p1 = new Punkt(3,4); Punkt p2 = new Punkt(3,4); Punkt2 q1 = new Punkt2(3,4); Punkt2 q2 = new Punkt2(3,4); Hva blir nå: public boolean equals(object o) { if (!(o instanceof Punkt2)) return false; Punkt2 p = (Punkt2) o; return x == p.x && y == p.y; p1.tostring(); Punkt@f5da06 q1.tostring(); p1.equals(p2); false q1.equals(q2); x = 3 y = 4 true 47
En lenket liste med noder (knuter) settinn(object x) Object taut( ) forste Navn: neste Type: Element Navn: neste Type: Element... Navn: denne Type: Object Navn: denne Type: Object Object-del Subklasse del(er) Objekter av class Object (++) 48