INF1010 forelesning Lenkelister II Dette skrivet inneholder en oversikt over det jeg planlegger å forelese på andre forlesning om lenkelister. Det inneholder stort sett programeksempler med kommentarer i samme rekkefølge som på forelesningen. Det er ikke sikkert vi kommer igjennom alt. Vi skal videreutvikle listeeksemplet som lå til grunn for første forelesning om lenkeliser. Vi har alt rettet feilene i det. Nå er målet å lage en ny listeklasse slik at vi kan lenke sammen andre objekter enn personobjekter. Vi vil også se på hvordan vi kan lage mange lenkelister med de samme objektene uten å måtte klone dem. Vi ønsker å utvikle en beholder for objekter (av ukjent type) hvor den interne datastruktur i beholderen er ei lenkeliste. Kort sagt en generell beholder for objekter. En rettet versjon av eksemplet fra sist: public class ListeAvPersoner { private Person f ø r s t e, s i s t e ; ListeAvPersoner ( ) { Person l h = new Person ( "LISTEHODE!! " ) ; f ø r s t e = l h ; s i s t e = l h ; a n t a l l = 0 ; public void settinnforan ( Person nypers ){ nypers. n e s t e = f ø r s t e. n e s t e ; f ø r s t e. n e s t e = nypers ; i f ( s i s t e. n e s t e == nypers ) // nyperson er ny siste! s i s t e = nypers ; public void settinnbak ( Person inn ){ public void s e t t I n n E t t e r ( Person denne, Person nypers ) { nypers. n e s t e = denne. n e s t e ; denne. n e s t e = nypers ; i f ( s i s t e. n e s t e == nypers ) // nyperson er ny siste! s i s t e = nypers ; public Person f i n n P e r s o n ( S t r i n g s ) { Person p = f ø r s t e. n e s t e ; for ( int i = a n t a l l ; i >0; i ) { i f ( p. hentnavn ( ). e q u a l s ( s ) ) return p ; else p = p. n e s t e ; return null ; Har fjernet metoder som ikke er vesentlig for det vi skal gjøre her (metoden som skriver ut bl.a.). Hvis vi ønsker å lagre viner i stedet for, må vi gjøre to endringer: klassen Vin må få en ny variabel Vin neste alle personvariable (personpekere) i listeklassen må byttes til Vin
public class ListeAvViner { private Vin f ø r s t e, s i s t e ; ListeAvViner ( ) { Vin l h = new Vin ( "LISTEHODE!! " ) ; public void settinnbak ( Vin inn ){ Hvis vi skulle lagre gaveobjekter (objekter som er instanser av klasser som implementerer grensesnittet Gave), hva må vi gjøre da? Den viktigste ulempen med listeklassen, er at objektene som lenkes sammen bare kan være med i ei liste av gangen, siden de bare har en nestepeker. Hvis vi laget flere nestepekere, kunne vi få til så mange lister som vi hadde pekere, men det er ingen tilfredsstillende løsning! Hvordan kan vi skrive programmet slik at vi kunne lage så mange lenkelister vi ville, som inneholdt personer f.eks., uten at vi måtte klone personobjektene for hver liste? Vi innfører følgende lille geniale klasse: class Node { Node ( Person p ) { o b j e k t e t = p ; Person p e r s o n o b j e k t e t ; Av denne klassen kan vi lage objekter (instanser) med to egenskaper: 1. de kan lenkes sammen gjennom lenka (pekeren) Node neste 2. hvert objekt kan peke på et personobjekt Disse objektene kaller vi knuter eller noder (fra latin nodus som betyr knute). Når et personobjekt skal inngå i ei liste med slike nodeobjekter, må vi i listeklassen lage et nytt nodeobjekt og sette dette til å peke på personobjektet og sette nodeobjektet inn i lista: class Node { // konstruktøren setter opp peker til personobjektet: Node ( Person p ) { p e r s o n o b j e k t e t = p ; Person p e r s o n o b j e k t e t ; public class ListeAvPersoner { ListeAvPersoner ( ) { Node l h = new Node (new Person ( "LISTEHODE!! " ) ) ; public void settinnbak ( Person nyperson ) { Node inn = new Node ( nyperson ) ;
Når et personobjekt settes inn lages det ikke en kopi av det, men en ny peker til objektet. Slik kan samme personobjekt pekes på fra «uendelig mange» lenkelister. Vi så ovenfor at det er vanseklig å bruke den første lenkelista til gaveobjekter(jf. oblig 3), siden Gave er et grensesnitt og ikke kan ha variable og da heller ingen nestepeker (Gave neste). Dette problemet forsvinner nå, siden nestepekeren ligger i knuteobjektet Node. Her er samme klasse for å lage lenkelister av gaveobjekter: class Node { // konstruktøren setter opp peker til gaveobjektet: Node ( Gave g ) { o b j e k t e t = g ; Gave g a v e o b j e k t e t ; public class ListeAvGaver { ListeAvGaver ( ) { Node l h = new Node (new Gave ( "LISTEHODE!! " ) ) ; // hva er galt her? public void settinnbak ( Gave nygave ) { Node inn = new Node ( nygave ) ; Legg merke til at metodene i listeklassen nå ikke forandrer på noen egenskaper i Person eller Gave. Metodene oppretter nye nodeobjekter og tilordner verdier til disse (neste og objektet). Disse to pekervariablene brukes bare av metodene inne i listeklassen. Siden programmet utenfor listeklassen ikke trenger å ha tilgang til nodeobjektene, er det god objektorientering å skjule denne klassen ved å gjøre den til en indre klasse, en klasse i klassen: public class ListeAvGaver { Node ( Gave g ) { o b j e k t e t = g ; Gave g a v e o b j e k t e t ; ListeAvGaver ( ) { Node l h = new Node (new Gave ( "LISTEHODE!! " ) ) ; // hva er galt her? public void settinnbak ( Gave nygave ) { Node inn = new Node ( nygave ) ;
Modifikatoren private gjør at Node er usynlig utenfor klassen (vi kan ikke deklarere en variabel av type Node utenfor klassen, med mindre vi har en annen klasse med samme navn der). Hvis klassen ikke er private, er den synlig som ListeAvGaver.Node, f.eks. kunne vi da ha laget et nodeobjekt med et gaveobjekt (f.eks. et objekt av Vin som implementerer Gave) utenfor klassen med ListeAvGaver. Node etfno = new ListeAvGaver. Node (new Vin ( " k v i t v i n " ) ) ; men dette har vi liten nytte av, derfor er det god programmeringsskikk og la indre klasser være skjult. Vi kommer ikke til å bruke indre klasser til mye mer enn dette i INF1010. Men fortsatt er listeklassen ikke i stand til å lagre annet en objekter av bestemte typer. ListeAvGaver kan riktignok lagre gaver av flere typer, så lenge objektene er instanser av klasser (eller subklasser til klasser) som implementerer grensesnittet Gave. Object-klassen er alle klassers mor eller superklasse. En variabel av type Object kan peke på objekter av alle klasser. public class ListeAvObjekter { Node ( Object obj ) { o b j e k t e t = obj ; Object o b j e k t e t ; ListeAvObjekter ( ) { Node l h = new Node (new Object ( ) ) ; public void settinnbak ( Object nyttobjekt ) { Node inn = new Node ( nyttobjekt ) ; public Object tautforan ( ) { // fjerner en node og returnerer objektet i f ( n!= null ) { f ø r s t e. n e s t e = n. n e s t e ; return n. o b j e k t e t ; else return null ; I denne lenkelista kan vi satte inn objekter av type Object og alle objekter av klasser som er subklasser til Object, dvs. alle objekter vi kan lage i Java! Så hvis denne fungerer for alt mulig, hva skal vi da med alternativer? Problemet med denne beholderen er at vi ikke vet mer om objektene i den enn at de har egenskapene til Object, jf. Javas API. Vi kan sette inn (metoden settinnbak) hva som helst. Til gjengjeld vet vi ikke hva vi får når vi tar ut (metoden tautforan). Her må vi evt. sjekke med
instanceof og typekonvertere. Hvis vi vet typen på objektet som hentes ut, kan vi typekonvertere direkte. Dette slipper vi hvis vi lar typen til objektene som inn i lista være (generisk) parameter til klassen: public class LenkeListe <T> { Node (T t ) { o b j e k t e t = t ; T o b j e k t e t ; LenkeListe ( ) { Node l h = new Node ( null ) ; // listehode f ø r s t e = l h ; s i s t e = l h ; a n t a l l = 0 ; public void settinnforan (T t ) { Node n = new Node ( t ) ; n. n e s t e = f ø r s t e. n e s t e ; f ø r s t e. n e s t e = n ; i f ( s i s t e. n e s t e == n ) // n er nytt sisteobjekt! s i s t e = n ; public T tautforan ( ) { i f ( n!= null ) { f ø r s t e. n e s t e = n. n e s t e ; return n. o b j e k t e t ; else return null ; Her er eksempler på bruk. Først en klasse hvor vi kan lagre objekter av klassen Bil. (Egentlig alle klasser som er subklasser av Bil eller subklasser til en klasse som implementerer Bil hvis Bil er et grensesnitt). LenkeListe <Bil > s t o r g a r a s j e = new LenkeListe <Bil >(); En lenkeliste med personer. Ved å lage flere slike kunne erstattet personarrayene i Person (fra obligene) med slike lenkelister av personer: LenkeListe <Person> minevenner = new LenkeListe <Person >(); LenkeListe <Person> k j e n n e r = new LenkeListe <Person >(); LenkeListe <Person> l i k e r I k k e = new LenkeListe <Person >(); Og et eksempel på en beholder for gaveobjekter à la oblig 3. Obs. Gave er her et grensesnitt: LenkeListe <Gave> gavebutikk = new LenkeListe <Gave >(); Hvis vi i listeklassen trenger å kjenne noen av egenskapene til objektene, er det mulig. Hvis vi vet at vi bare ønsker å lagre objekter av klasser som implementerer Gave-grensesnittet, kan vi skrive public class LenkeListe <T extends Gave> { Node (T t ) { o b j e k t e t = t ; T o b j e k t e t ; LenkeListe ( ) {
Node l h = new Node ( null ) ; // listehode f ø r s t e = l h ; s i s t e = l h ; a n t a l l = 0 ; public void settinnforan (T t ) { Node n = new Node ( t ) ; n. n e s t e = f ø r s t e. n e s t e ; f ø r s t e. n e s t e = n ; i f ( s i s t e. n e s t e == n ) // n er nytt sisteobjekt! s i s t e = n ; public T tautforan ( ) { i f ( n!= null ) { f ø r s t e. n e s t e = n. n e s t e ; return n. o b j e k t e t ; else return null ; Her kan vi få tak i gaveegenskaper (fra grensesnittet) inne i klassen, f.eks. slik: public T f inngaveikategori ( S t r i n g s ) { for ( int i = a n t a l l ; i >0; i ) { i f ( n. o b j e k t e t. k a t e g o r i ( ). e q u a l s ( s ) ) return n. o b j e k t e t ; else n = n. n e s t e ; return null ;