Refleksion i Java. 8. juli 2003
|
|
|
- Bjarne Clausen
- 10 år siden
- Visninger:
Transkript
1 Refleksion i Java Udarbejdet af: Jesper Tejlgaard Pedersen Anders Baumann Tine Thorn IT-højskolen i København 4-ugersprojekt F2001 Vejleder: Kasper Østerbye 8. juli
2 Indhold 1 Forord 3 2 Indledning Problemstilling Refleksionsbegreber Refleksion Reifikation Introspektion Manipulation Strukturel refleksion Opførselsrefleksion Kompileringstids-, load-tids- og køretidsrefleksion Kompileringstidsrefleksion Load-tidsrefleksion Køretidsrefleksion Baseniveau og metaniveau Baseniveau Metaniveau Metaobjekter og metaklasser Metaobjektprotokol (MOP) Opsamling på refleksionsbegreber Refleksion i Java Strukturel refleksion Klasse-loader Dynamiske proxy klasser Opsamling på Java s refleksion Objekt Browser Formål Design Implementation Test af systemet Brugervejledning Opsamling på det praktiske arbejde
3 6 Refleksion i MetaJava Design af MetaJava Event-modellen Forbindelse af metaniveau og baseniveau Metaniveauinterface og event-generering Implementation Skyggeklasser Anvendelse Performance og pladsforbrug Opsamling på MetaJava s refleksion Refleksion i OpenJava Design af OpenJava s MOP Oversættelse og metaobjekter API Anvendelse Opsamling på OpenJava s refleksion Refleksion i Javassist Design Implementation Anvendelse Opsamling på Javassist s refleksion Diskussion Analyse af skema Sammenligning af de enkelte sprog Analyse af performance Konklusion 38 A Appendix: Klassediagrammer 40 B Appendix: Kildekode 41 B.1 Pakken app.browser B.2 Pakken app.gui B.3 Pakken test.browser
4 1 Forord Denne projektrapport er udarbejdet i maj 2001 under vejledning af Kasper Østerbye i forbindelse med et fire-ugersprojekt på IT-højskolen i København. Projektet omhandler refleksion i forbindelse med programmeringssprogene Java og OpenJava samt to prototype-implementationer af Java, nemlig MetaJava og Javassist. Endvidere omhandler projektet design og implementation af en objektbrowser, der kan anvendes i forbindelse med debugging. Kildekoden til objektbrowseren kan ses på webadressen: tejl/objektbrowser/src Klassefilerne til objektbrowseren (objectbrowser.jar) ligger på webadressen: tejl/objektbrowser 4
5 2 Indledning Refleksion er en mekanisme, der gør et program i stand til at se og ændre dels sin egen kode, dels programmeringssprogets syntaks og semantik. Igennem refleksion er et program dermed i stand til at tilpasse sig varierende omgivelser. Dette er en egenskab, som kan anvendes i flere forskellige sammenhænge. Blandt andet kan man benytte refleksion til at få information om objekter, dvs. hvilken klasse objektet er en instans af, hvilke metoder og felter objektet har, samt hvad værdien af disse er osv. Denne information kan eksempelvis benyttes af debuggere og GUIværktøjer. 2.1 Problemstilling Det overordnede formål med dette projekt er, selvstændigt at sætte os ind i emnet refleksion. Dette gøres dels ved et litteraturstudium, dels gennem design og implementering af en simpel objektbrowser. Grundet tidsrammen på fire uger, har vi valgt kun at undersøge refleksion i forbindelse med programmeringssproget Java og forskellige udvidelser af Java (MetaJava og Javassist) samt OpenJava. Eftersom vi ikke havde noget egentligt kendskab til refleksion inden vi påbegyndte dette projekt (projektet bygger ikke videre på et kursusforløb), har vi valgt at fokusere på de procesmæssige aspekter frem for de produktmæssige. Der er derfor lagt stor vægt på at opnå en forståelse af begrebet refleksion frem for den praktiske anvendelse af refleksion. Formålet med at designe og implementere en objektbrowser i Java var udelukkende at stifte bekendtskab med Java s refleksions-api. Det har ikke været et mål at implementere en fuldt ud anvendelig objektbrowser til brug i debuggingsammenhænge. 3 Refleksionsbegreber Formålet med dette afsnit er kort, at introducere de for vores projekt væsentligste begreber vedrørende refleksion. 3.1 Refleksion Refleksion er et systems evne til at ræssonere og handle på baggrund af sig selv, samt kunne tilpasse sig forskellige omstændigheder [1]. Programmet har altså derigennem en indbygget evne til at kunne kigge på, eller ændre på sin egen syntaks, 5
6 semantik eller implementation [2]. Man taler om forskellige former for refleksion, bla. introspektiv, manipulerende, kompileringstids-, load-tids- og køretidsreflekion. Disse begreber defineres i de nedenstående afsnit. 3.2 Reifikation Reifikation betyder at gøre noget tilgængeligt, som ikke normalt er tilgængeligt i programmet eller er skjult for programmøren [1]. Dvs. reifikation er en proces hvor en del af et program eller sprog, der før ikke var tilgængeligt, nu gøres tilgængeligt, ved at bruge en datastruktur der udtrykkes i sproget selv [2]. Herefter har programmet mulighed for at inspicere sig selv (metainformation er tilgængelig). 3.3 Introspektion Introspektion er en evne til at kunne undersøge egenskaber for datastrukturer i et program, f.eks. klasser [3, 4]. 3.4 Manipulation Manipulation 1 af et program betyder, at programmets opførsel eller struktur ændres. 3.5 Strukturel refleksion Strukturel refleksion er evnen til at se eller ændre de datastrukturer der benyttes af et program [4], dvs. evnen til at se eller ændre det abstrakte syntakstræ. Et eksempel på strukturel refleksion er ændring af definitionen af datastrukturer for klasser og metoder [4]. Man kan også sige, at strukturel refleksion er et sprogs evne til at tilbyde fuldstændig reifikation af det kørende program samt af dets abstrakte datatyper [2]. Strukturel refleksion kan både være introspektiv og manipulerende, og der kan optræde strukturel refleksion i alle faser af eksekveringen af et program. Javassist er et eksempel på et sprog der understøtter strukturel refleksion. 3.6 Opførselsrefleksion Opførselsrefleksion er evnen til at kunne se og/eller ændre et programs opførsel, f.eks. ved at ændre metoders opførsel [4]. Derved kan opførselsrefleksion både være introspektiv og manipulerende. Opførselsrefleksion tillader ikke, i modsætning til strukturel refleksion, ændringer af statiske datastrukturer [4]. 1 Dette er vores oversættelse af det engelske begreb intercession 6
7 MetaJava er et eksempel på et sprog der understøtter opførselsrefleksion. 3.7 Kompileringstids-, load-tids- og køretidsrefleksion Afhængigt af hvornår binding mellem basesystem og metasystem foregår, kan man tale om hhv. kompileringstids-, load-tids- og køretidsrefleksion. Der kan i alle disse tre reflektionsformer foretages både strukturel refleksion og opførselsrefleksion Kompileringstidsrefleksion Med kompileringtidsrefleksion er det muligt at udnytte refleksion i et program, når programmet kompileres. OpenJava er et eksempel på et sprog, der understøtter kompileringstidsrefleksion Load-tidsrefleksion Ved Load-tidsrefleksion bliver refleksion udført på en klasse, når den loades af klasse-loaderen. [4]. Det er altså i klasse-loaderen at refleksion foretages. Javassist er et eksempel på et sprog, der understøtter load-tidsrefleksion Køretidsrefleksion Med køretidsrefleksion er der mulighed for refleksion på køretidspunktet, dvs. mens programmet rent faktisk eksekveres. MetaJava er et eksempel på et sprog, der understøtter køretidssrefleksion. 3.8 Baseniveau og metaniveau I forbindelse med refleksion indføres der ofte et baseniveau og et metaniveau. Disse gør det muligt at adskille funktionel kode fra ikke-funktionel kode. Funktionel kode er den kode, der vedrører operationer, som foregår i basesystemet 2. Ikke-funktionel kode er placeret i metasystemet, og har til formål at kontrollere og holde øje med, hvordan den funktionelle kode bliver udført (jvf. figur 1) [5, 1] Baseniveau På baseniveauet findes det program, der skal undersøges eller ændres, hvilket vil sige applikationsprogrammet, hvorpå refleksion skal foretages [3]. 2 Dvs. i det egentlige applikationsprogram 7
8 Figur 1: Metasystem og basesystem. Figuren viser sammenhængen mellem metasystemet og basesystemet. Frit efter [6] Metaniveau På metaniveauet findes den del af koden, der tillader et program at reflektere over sig selv. Det er på metaniveauet, at introspektion såvel som manipulation af programmets strukturelle og opførelsesmæssige egenskaber foretages. For at kunne understøtte refleksive operationer, foretages der på metaniveauet en reificering af programmet på baseniveauet Metaobjekter og metaklasser Et metaobjekt er en instans af en metaklasse. Metaobjektet indeholder information om entiter på baseniveauet, og kan kontrollere udførslen af disse baseentiteter [1]. Et eksempel på en metaklasse er Java s klasse Class. 8
9 3.8.4 Metaobjektprotokol (MOP) En MOP er en protokol, der specificerer et interface til metaniveauet, dvs hvordan metaklasser implementeres, og hvordan metaobjekter tilknyttes baseniveauet (dvs. hvordan der oprettes metaobjekter) [3]. Der er både kompileringstids-, load-tids- og køretids-mop s. Afhængigt af i hvilken fase af programmets eksekvering man er, vil de refleksive beregninger ske på kompileringstidspunktet, load-tidspunktet eller køretidspunktet [3]. OpenJava er en kompileringstids-mop, mens Javassist er en load-tids-mop og MetaJava en køretids-mop. 3.9 Opsamling på refleksionsbegreber Eftersom der er en vis ortogonalitet mellem nogle af de gennemgåede refleksionsbegreber, har vi valgt at systematisere disse i et skema (jvf. nedenstående skema 1). Vi vil undervejs i rapporten placere hvert enkelt programmeringssprog i skemaet, og vil benytte skemaet som udgangspunkt for vores diskussion. Refleksion Kompileringstid Load-tid Køretid Introspektion Opførsel Strukturel Manipulation Opførsel Strukturel Tabel 1: Skema til systematisering af refleksionsbegreberne 4 Refleksion i Java Java er et programmeringssprog der understøtter introspektiv strukturel refleksion på køretid, hvor ved der er mulighed for at få information om et programs datastrukturer under eksekvering af programmet. Det er således muligt at undersøge et objekts tilstand, udføre et metodekald specificeret ved en streng, samt få information om hvilke felter og metoder objektets klasse har. Den strukturelle refleksion beskrives i afsnit 4.1. Udover den introspektive strukturelle reflektion gives der i Java 1.3 mulighed for en begrænset form for opførselsrefleksion. Dette omtales i afsnit
10 4.1 Strukturel refleksion Den centrale klasse i Java s standard refleksions-api er Class [7]. En instans af denne klasse repræsenterer eller reflekterer en klasse på køretid. Ved at kalde dens metoder kan man få returneret instanser af klasserne: Constructor, Method, Field og Class. Man kan så bruge disse objekter til bl.a. at få information om klassens konstruktorer, metoder, felter og superklasser. Eksempelvis returnerer metoden getmethods() i Class et array af instanser af klassen Method. På hver af disse instanser kan man efterfølgende kalde diverse metoder, f.eks. getparametertypes(), der returnerer et array af Class, hvor hver instans repræsenterer typen af en inputparameter. Instanser af Class repræsenterer også interfaces, og man kan derfor også hente oplysninger om modifiers, metoder og konstanter til et interface. Med Java s refleksions-api er det også muligt at oprette instanser af en klasse, hvor navnet på klassen først er kendt på køretidspunktet. Dette gøres med den statiske metode Class.forname(String name), der returnerer et objekt af typen Class. Det er også muligt at se og ændre feltværdier samt kalde metoder, hvis navne først er kendt på køretidspunktet. Et eksempel på en faktisk anvendelse af denne funktionalitet kan ses i afsnit 5, hvor vi har implementeret en såkaldt objektbrowser, der gør det muligt for brugeren at se tilstanden af et givent objekt på et hvilket som helst tidspunkt under programafviklingen. Kald af en metode, hvor metodenavnet først er kendt på køretid er bl.a. anvendelig i en debugger, der tillader brugeren at vælge hvilke metoder han/hun vil kalde under selve programudførslen [8]. 4.2 Klasse-loader Java s virtuelle maskine benytter en klasse-loader til at loade.class-filer. Det er i Java muligt at lave sin egen klasse-loader, ved at definere en subklasse af den abstrakte klasse ClassLoader. I denne subklasse er det muligt at ændre et programs opførsel ved at modificere den loadede bytekode. Denne teknik benyttes af Javassist (se afsnit 8). 4.3 Dynamiske proxy klasser I Java 1.3 er det blevet muligt at bruge en tilnærmet form for opførselsrefleksion ved brug af dynamiske proxy klasser. Disse tillader, at et program på køretid implementerer nye interfaces ved at videresende alle metodekald til en instans af klassen InvocationHandler. Dynamiske proxy klasser kan således tilføje ekstra 10
11 funktionalitet til eksisterende klasser uden at ændre i disse, og de giver derfor programmører mulighed for at tilføje metode-generisk funktionalitet til deres system [9]. Et eksempel på en sådan funktionalitet er logning. I forbindelse med debugging ønsker man ofte at logge en klasses metodekald. Som et eksempel kigger vi på følgende program: interface X public void foo(); public void bar(); class XImpl implements X private int x = 0; private int y = 0; public void foox = y * 2; public void bary = x + 5; Normalt vile man implementere logning ved brug af designmønstret decorator, der gør brug af en såkaldt wrapper-klasse [10]: class XLogger implements X private X _x; public XLogger(X x) _x = x; public void foo()( System.out.println("foo called"); _x.foo(); ) public void bar() System.out.println("bar called"); _x.bar(); Som det ses tilføjer XLogger ekstra funtionalitet til XImpl uden at rette i selve klassen. Denne løsning er dog ikke optimal. For det første er det besværligt at skulle skrive hele klassen XImpl en gang til. For det andet er logning et generisk problem, men det er ovenstående løsning ikke. Hvis vi ønsker at logge en anden klasse Y 11
12 skal vi således skrive en ny wrapper-klasse for Y. Hvis log-funktionalitet skal være generisk og derfor uafhænging af interfacet X, så skal vi bruge en dynamisk proxy klasse. Vi kan få en instans af en dynamisk proxy klasse, der implementerer et givent antal interfaces ved at kalde den statiske metode: Proxy.newProxyInstance(ClassLoader classloadertouse, Class[] interfacestoimplement, InvocationHandler objtodelegateto) Metoden returnerer en dynamisk proxy klasse, der implementerer de interfaces, der er angivet i parameteren interfacestoimplement. Proxyklassen vil delegere alle metodekald i disse interfaces videre til den associerede InvocationHandlers eneste metode: public Object invoke(object proxy, Method meth, Object[]args) Det første argument i denne metode er den proxy-instans, som metoden blev kaldt på. Det andet argument repræsenterer den kaldte metode og det sidste de argumenter, som blev givet som parametre til metodekaldet. Det er i metoden invoke, at man specificerer man hvad der skal ske ved de enkelte metodekald. For at benytte en dynamiske proxy klasse skal vi have en instans af en klasse, der implementerer InvocationHandler-interfacet, og vores proxyklasse vil så sende metodekald videre til dette objekt. class Logger implements InvocationHandler private Object delegate; public Logger(Object o) delegate = o; public Object invoke(object proxy, Method meth, Object[] args) System.out.println(meth.getName()); // Skriv til log. try return meth.invoke(delegate, args); catch (InvocationTargetException e) throw e.gettargetexception(); public static void main(string[] args) 12
13 X logger = (X) Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[] X.class, new Logger(new XImpl())); I vores hovedprogram opretter vi ved brug af den statiske metode Proxy.newProxyInstance en ny proxy, der implementerer de interfaces, der er angivet som anden parameter. I vores tilfælde drejer det sig om interfacet X. Alle kald af X s metoder vil blive delegeret videre til en instans af Logger, som er tredie parameter. For at invoke i Logger kan delegere det modtaget metodekald videre efter at der er blevet skrevet i loggen, er den nød til at eje en instans af en klasse, der implementere det interface, hvor metodekaldet stammer fra. Derfor tager Logger s konstruktør et objekt af typen Object som inputparameter. I vores tilfælde giver vi en instans af klassen XImpl. Som det ses bruger invoke denne instans som inputparameter i det refleksive metodekald: return meth.invoke(delegate, args); Herved vil metoden meth i objektet delegate blive kaldt med args som parametre. Vi har med ovenstående løsning implementeret en generisk logger, der kan bruges på en hvilken som helt klasse. Dynamiske proxy klasser har dog, ligesom resten af Java s refleksions-api, et vist performance overhead på køretidspunktet. 4.4 Opsamling på Java s refleksion Efter at have gennemgået Java, kan vi konkludere at dette programmeringssprog understøtter følgende refleksive egenskaber (jvf. skema 1 i afsnit 3.9): Java understøtter introspektiv opførselsrefleksion og strukturel refleksion på køretidspunktet, da det er muligt at undersøge metoder, instansvariable, hvilken klasse et objekt er en instans af osv. på køretidspunktet. Endvidere understøtter Java manipulerende opførselsrefleksion på køretidspunktet, idet det vha. dynamiske proxy-klasser er muligt at ændre metoder. 5 Objekt Browser 5.1 Formål For at få praktisk erfaring med Javas refleksions-api har vi valgt at implementere en objektbrowser. Ideen med objektbrowseren er at den skal fungere som et hjælpeprogram til brug ved debugging af andre programmer. Brugeren skal på et 13
14 hvilket som helst sted i sit program kunne kalde en statisk metode i browseren med et vilkårligt objekt som input, og objektbrowseren skal så vise objektets instanser og værdierne for disse. 5.2 Design På et overordnet plan består vores system af to dele: en præsentationsdel, der står for præsentation af uddata for brugeren og selve beregningsdelen, hvor vi benytter refleksion til at udtrække de relevante oplysninger om inddata. Denne separation af præsentation og model giver os både et pænt og overskueligt design og giver os samtidig en mulighed for at præsentere uddata på flere forskellige måder. Vi har således valgt at implementere en simpel tekstbaseret præsentation hvor uddata bliver skrevet til Shell en, samt en mere avanceret GUI-version, hvor brugeren har følgende muligheder: Et tryk på en reference vil vise tilstanden af objektet, som referencen refererer til. Et tryk på en klasse vil vise information om klassen: Pakkenavn. Statiske felter. Superklasse. Hvilke interfaces klassen implementerer. Metoder. Det er muligt at browse både frem og tilbage mellem uddata. Vores system har kun et vindue, og når brugeren trykker på et link, vil det nye indhold overskrive det aktuelle. Ved tryk på tilbage-knappen vil det forrige indhold blive vist. Alternativ kunne man havde valgt en løsning, hvor hvert klik fra brugeren åbnede et nyt vindue. Vi mener dog, at vores løsning er enklere og mere brugervenlig. Frem- og tilbageknapperne har vi valgt at placere i en toolbar øverst til venstre i browservinduet, ligesom de er i f.eks. Netscape. For at tydeliggøre for brugeren hvilke ord, det er muligt at trykke på, er alle links markeret med en lilla farve. 5.3 Implementation Vi vil i det følgende gennemgå de vigtigste klasser i vores system (se figur 14 og 15 i appendix A): 14
15 Figur 2: GUI-version af Objectbrowser Figur 3: Shell-version af Objectbrowser ObjectInfo Denne klasse indeholder information om et objekts tilstand. Dens konstruktor tager et objekt af typen Object som input, og udtrækker information om objektets felter og deres værdi ved brug af Java s indbyggede refleksions-api. Til opbevaring af objektets felter anvender ObjectInfo et array af instanser af klassen FieldInfo. 15
16 ClassInfo Denne klasse indeholder information om en klasse svarende til den information brugeren får præsenteret, når han/hun klikker på et klasse-link. Til opbevaring af information om metoder, kontruktorer og statiske felter bruger ClassInfo instanser af klasserne MethodInfo, ConstructorInfo og StaticFieldInfo. ShellBrowser Det er denne klasse, som brugeren skal benytte, hvis han/hun ønsker at få uddata præsenteret i Shell. Klassen benytter en instans af klassen ShellPrinter til at få data udskrevet på skærmen. GUIBrowser Svarer til ShellBrowser, men her bliver uddata vist i et Swing-vindue. BrowserWindow Denne klasse repræsenterer selve den grafiske udgave af browservinduet, som vi har implementeret i Swing. Vi har valgt at lade klassen være en singleton, da vi vil være sikre på, at det altid er den samme instans af klassen vi får fat på, når vi indsætter ny tekst i browservinduet. ObjectInfoCanvas Denne klasse er ansvarlig for at vise et objekts tilstand i en Swing frame. ClassInfoCanvas Denne klasse står for at vise information om en klasse i en Swing frame. For fuldstændig selv at kunne bestemme browservinduets udseende, har vi valgt at bruge specialiseringer af Swings indbyggede klasser. Vi har således benyttet klasser der nedarver fra JLabel, JButton, JPanel og JFrame. For at vores browser kan give et øjebliksbillede af et objekts tilstand, er vi nødt til at standse det kaldende program, indtil browservinduet bliver lukket. Hvis programmet i stedet fortsatte sin udførsel, ville informationen i browservinduet ikke mere være korrekt, da programmets videre forløb kunne ændre det aktuelle objekts tilstand. For at standse det kaldende program har vi derfor implementeret en klassen WaitMonitor, der har to metoder: rest() og goon(). BrowserWindow kalder rest() lige efter at det har åbnet browservinduet, hvilket medfører at det kaldende programs tråd venter i WaitMonitor indtil vinduet bliver lukket og goon() kaldes. 5.4 Test af systemet Vi har ved brug af JUnit skrevet unit test for alle klasser i pakken app.browser (jvf. figur 14 i appendix A). Dvs. de klasser i vores system, der har med refleksion at gøre. Vi har valgt at benyttet testmetoden fra Extreme Programming, hvor testen for hver klasse skrives før selve klassen. Vi testede vores programmer vha. en 16
17 dummy-klasse, og vi oplevede ingen særlige testproblemer, der knyttede sig til, at testene blev udført på et refleksivt program. 5.5 Brugervejledning For at benytte vores objektbrowser, skal filen objectbrowser.jar inkluderes i classpath, og brugeren kan så undersøge et givent objekt o ved enten at kalde ShellBrowser.browse(o) eller GUIBrowser.Browse(o), der ligger i pakken app.browser. 5.6 Opsamling på det praktiske arbejde Introspektiv strukturel refleksion er blevet afprøvet i praksis ved at bruge Java s reflektions-api til at designe og implementere en objektbrowser. Den implementerede objektbrowser giver mulighed for, at undersøge et objekts tilstand samt modtage information om objektets klasse, herunder klassens metoder, konstruktorer, statiske felter og superklasse. Derimod er det i vores objektbrowser ikke muligt at ændre strukturen af klassen, eftersom Java ikke understøtter denne form for manipulerende refleksion. 6 Refleksion i MetaJava MetaJava 3 er en prototype-implementation af Java, der bygger på en uvidelse af Java s virtuelle maskine (kaldet MetaJava Virtual Machine; MJVM). Denne udvidede JVM gør MetaJava i stand til at understøtte opførselsrefleksion på køretid. Intentionen med MetaJava var at lave en refleksiv arkitektur til Java, hvor både strukturel refleksion og opførselsrefleksion var understøttet 4 [1]. Dette var årsag til, at der blev introduceret et metasystem, således at det var muligt at adskille funktionel kode fra ikke-funktionel kode (jvf. afsnit 3.8). 6.1 Design af MetaJava Modellen for MetaJava bygger som før nævnt på et basesystem samt et metasystem. Derudover opererer modellen med begreberne event, metaniveauinterface 3 Senere blev MetaJava omdøbt til metaxa, men vi vil i dette projekt benytte det oprindelige navn MetaJava 4 I MetaJava benyttes begrebet strukturel refleksion som synonym for introspektiv strukturel refleksion, dvs. mulighed for at undersøge egenskaber for strukturen at et program, f.eks. struktur af klasser. 17
18 samt skyggeklasse. I det følgende defineres disse begreber og MetaJava s model beskrives Event-modellen Når man ønsker at understøtte refleksion, er det essentielt at kunne overføre kontrol fra basesystemet til metasystemet. Under eksekvering af applikationsprogrammet udføres der i basesystemet forskellige operationer så som metodekald, læsning/skrivning af instansvariable, dannelse af nye objekter, loading af klasser o.s.v. I den virtuelle maskine er der implementeret default-opførsler for hver af disse operationer. En overførsel af kontrol fra basesystem til metasystem betyder, at det er metasystemet, der afgør hvordan operationerne i basesystemet skal udføres (det kan både være en default-opførsel eller en af metasystemet bestemt opførsel). Til at overføre kontrol fra basesystem til metasystem benyttes der i MetaJava en event-model. Hvis et metaobjekt implementerer en given operation 5, og denne operation udføres i basesystemet, vil der opstå en event. Denne event bliver videredelegeret til metasystemet, hvor den vil blive evalueret og håndteret på passende vis (jvf. figur 4) [6, 11]. De forskellige events der eksisterer for de forskellige operationer kan ses i tabel Forbindelse af metaniveau og baseniveau Måden hvorpå baseniveau og metaniveau knyttes sammen, er ved at tilknytte et metaobjekt til en baseniveauentitet, dvs. en klasse, et objekt eller en reference i basesystemet. Når et metaobjekt er tilknyttet en baseniveauentitet betyder det, at det er dette metaobjekt, der modtager og håndtere alle de events, der vedrører den givne baseniveauentitet (jvf. figur 5) [6, 11, 1]. Metaobjekter kan have forskellige funktioner afhængigt af, hvad de skal bruges til, og hvilken baseniveauentitet de er tilknyttet. Er metaobjektet tilknyttet et baseobjekt, vil funktionen af metaobjektet som regel være at ændre semantikken for dette baseobjekt, og derved gøre objektet refleksivt [6, 11]. Eksempelvis ville metaobjektet kunne reificere alle de metodekald, der stammer fra det specifikke baseobjekt [1]. Ændringer i semantikken for et base-objekt kan opnås ved at modificere objektets klasse (se nedenstående beskrivelse af skyggeklasser). Er metaobjektet tilknyttet en reference, vil det kun være events, der genereres på baggrund af operationer, som bruger denne reference der videredelegeres [1]. Herefter kan metaobjektet på samme måde, som hvis det var tilknyttet et baseobjekt, reificere metodekald, der benytter den givne reference. 5 se afsnit om event-generering 18
19 Figur 4: Event-model. På baggrund af operationer (f.eks. metode-kald, tilgang til felter, klasse-loading osv.) i basesystemet skabes der events, hvis et metaobjekt implementerer en given operation. Event sendes afsted til metasystemet, hvor det er op til det tilknyttede metaobjekt at evaluere og håndtere denne event. Herved overføres der kontrol fra basesystemet til metasystemet. Endelig vil tilknytning af et metaobjekt til en klasse betyde, at metaobjektet modtager de events, der genereres for alle instanser af denne klasse [11, 1]. Dette betyder, at alle instanser af klassen gøres refleksive [5, 11]. Når et metaobjekt skal knyttes til en baseniveauentitet, kan det benytte en af følgende tre metoder [1]: void attachobject(object o) Knytter metaobjektet til et objekt. Object attachreference(object o) Knytter metaobjektet til en objektrefe- 19
20 Event enter-method(object, method, arguments) create-object(class) read-field(object, field) write-field(object, field, value) load-class(classname) aquire-objectlock(object) Beskrivelse Metoden method kaldes på objektet object med argumenterne arguments. Der laves en instans af klassen class. Objektet object s felt field læses. Værdien value skrives i objektet object s felt field. Klassen med navnet classname benyttes for første gang og skal derfor loades. Der optages lås på objektet object. Objektet object s lås frigives. release-objectlock(object) Tabel 2: Events. Tabellen viser hvilke events, der kan genereres på baggrund af operationer i basesystemet. rence. I MetaJava er det ikke muligt at ændre semantikken af en reference. Denne metode returnerer i stedet en ny reference, der besidder de refleksive egenskaber. void attachclass(class c) Knytter metaobjektet til en klasse Metaniveauinterface og event-generering MetaJava er som sagt en udvidelse af Java s virtuelle maskine, og denne udvidelse består af to dele. Den ene del er et såkaldt metaniveauinterface 6 mens den anden del er et event-genererende lag (jvf. figur 6). For at metaobjekter kan få mulighed for at foretage manipulationer af den virtuelle maskine, eller mulighed for introspektiv strukturel refleksion, har metaobjekterne adgang til en række metoder[1, 12]. Det er disse metoder, der har fået betegnelsen metanivauinterface. Alle metoder i metaniveauinterfacet er implementeret som native metoder, hvilket betyder, at metoderne er processorspecifikke. Oven på metaniveauinterfacet ligger det event-genererende lag (jvf. figur 6). Dette lag består af et sæt af metoder skrevet i Java, der arves af alle metaobjekter 6 Dette er en direkte oversættelse af begrebet meta-level interface (MLI). 20
21 Figur 5: Tilknytning af metaobjekt til baseniveauentitet. Et metaobjekt kan tilknyttes et baseobjekt, en basereference eller baseklasse. Tilknytningen betyder, at det tilknyttede metaobjekt modtager de events der vedrører den givne baseniveauentitet. Frit efter [1]. Figur 6: Model over MetaJava s virtuelle maskine (MJVM). 21
22 (instanser af klassen MetaObject). Det event-genererende lag benytter introspektiv strukturel refleksion når operationerne fra basesystemet skal reificeres, dvs. når der skal genereres events [5, 1]. Det er altså vha. dette event-genererende lag at det er muligt at understøtte opførselsrefleksion i MetaJava. Hvis metaobjektet er interesseret i, at baseobjektet skal opføre sig på en specifik måde, kan metaobjektet registrere en event hos det event-genererende lag, med en af følgende metoder: void registereventmethodcall(object o, int type, String m[]) Nødvendig hvis metaobjekt skal kunne modtage events for indgående metodekald. o er baseniveauobjektet, m[] indeholder en konkatenering af metodenavne og signaturer og type specificerer det ønskede notifikationsskema. Der er to slags notifikationsskemaer i MetaJava; before/after eller handler. Et before/after-notifikationsskema, gør det enkelte metaobjekt i stand til at verificere constraints før eller efter metoden eksekveres, eller i stand til at forsinke eksekvering af metoden. I et handler-notifikationsskema, får en handler information om alle baseniveaumetoder, og det er så denne handler, der er ansvarlig for eksekveringen af baseniveaumetoden. void registereventmethodcallout(object o, String methods[]) Nødvendig hvis metaobjekt skal kunne modtage events for udgående metodekald 7. o er baseniveauobjektet og methods[] indeholder en konkatanering af metodenavne og signaturer. void registereventfieldaccess(object o, String fields[]) Nødvendig hvis metaobjekt skal modtage en event, når en instansvariabel tilgås. o er baseniveauobjektet og fields[] indeholder en konkatenering af navne og typer på instansvariable. void registereventobjectlock(object o) Nødvendig hvis metaobjekt skal modtage en event, når der optages en lås på baseobjektet o. void registereventobjectcreation(class c) Nødvendig hvis metaobjektet skal modtage en event, når der laves et nyt objekt af klassen c. void registereventclassloading(string classname) Nødvendig hvis metaobjektet skal modtage en event når klassen ved navn classname loades. 6.2 Implementation I det følgende vil vi kort gennemgå hvordan MetaJava er implementeret, og hvordan de problemer, der er opstået under implementationen, er håndteret. 7 Metodekald der udføres af baseniveauobjektet på andre objekter betegnes udgående metodekald. 22
23 6.2.1 Skyggeklasser Et problem ved det overordnede design af MetaJava er, at de semantiske ændringer et metaobjekt tilfører et baseobjekt, ikke bør have nogen indflydelse på andre baseobjekter af samme klasse. For at løse dette problem, blev der indført nogle såkaldte skyggeklasser [11]. En skyggeklasse er basalt set blot en klon af et givet baseobjekts baseklasse, og denne klon muliggør ændringer på enkelt-objektniveau. En skyggeklasse er i [11] defineret ved følgende egenskaber: På baseniveau er der ingen forskel på baseklassen og skyggeklassen. Skyggeklassen er identisk med baseklassen bortset fra ændringer foretaget af metasystemet. Statiske felter og metoder er delt mellem baseklassen og skyggeklassen. Måden hvorpå skyggeklasser dannes, er ved at et metaobjekt tilknyttes et baseobjekt. Skyggeklassen har en reference til baseklassen, hvilket betyder, at det refleksive baseobjekt er typekompatibelt med det oprindelige baseobjekt. Skyggeklassen har endvidere en reference til det metaobjekt, der var skyld i dannelsen af skyggeklassen. Efter dannelsen af skyggeklassen vil baseobjektets klassereference blive sat til at pege på skyggeklassen frem for baseklassen (jvf. figur 7) [1, 11]. Det er muligt at tilknytte flere metaobjekter til samme baseobjekt. Er dette tilfældet, vil der blive dannet et hieraki af skyggeklasser, og der vil blive lavet et tårn af metaobjekter (jvf. figur 8) [1, 11]. Det omtalte tårn af metaobjekter svarer dog ikke til et metaobjekttårn i den traditionelle forstand. Ofte vil betydningen af metaobjekttårn (også kendt som et refleksivt tårn) nemlig være en konstruktion bestående af metaobjekter der placeres over hinanden. Hvert metaobjekt sørger så for en reificering af det metaobjekt, der befinder sig ét niveau nedenunder, og det nederste metaobjekt reificerer baseobjekter [2, 1]. I MetaJava vil det metaobjekt, der sidst er blevet tilknyttet baseobjektet blive aktiveret først. Hvis dette metaobjekt vælger at fortsætte med baseobjektets default-opførsel, bliver det næste metaobjekt i tårnet (metaobjektet nedenunder) aktiveret [11]. Hvis et metaobjekt ikke fortsætter med defaultopførslen, vil de øvrige lavereliggende metaobjekter ikke have nogen effekt [11]. Dette betyder, at rækkefølgen af metaobjekterne i metaobjekttårnet er afgørende for, hvad der egentlig kommer til at ske. Der er flere ting man må sikre sig, når man benytter skyggeklasser. For det første er det nødvendigt at sikre, at der opretholdes en form for konsistens mellem baseklassen og skyggeklassen. Dette bliver løst ved at dele statiske felter og metoder mellem baseklassen og skyggeklassen [11]. For det andet er det nødvendigt at kunne fjerne skyggeklasser med garbage collectoren (GC), hvis de ikke 23
24 Figur 7: Dannelse af skyggeklasse. A: Baseobjekterne A og B er begge instanser af klassen C. B: Tilknytning af metaobjekt til objekt B medfører en dannelse af skyggeklasse C. Skyggeklassens baseklassereference bliver sat til at pege på baseklassen C, og dens metaobjektreference sættes til at pege på det tilknyttede metaobjekt. Objekt B s klassereference bliver sat til at pege på skyggeklassen C. Frit efter [11]. længere bliver benyttet (dvs. hvis metaobjektet ikke længere er knyttet til baseobjektet). Måden hvorpå dette problem løses, er ved at GC mærker alle klasser i metaobjekttårnet (som også kan ses som et tårn af skyggeklasser) ved at følge baseklassereferencen. 6.3 Anvendelse For at få et større kendskab til køretidsrefleksion ønskede vi at afprøve dette i praksis vha. MetaJava. Desværre bliver MetaJava ikke længere supporteret, og prototypen distribueres ikke. Der er lavet forskellige eksempler, der illustrerer MetaJava s anvendelighed. Et eksempel er en klasse, der kan browse metoder; klassen MetaTrace [1, 11]. Dette er et typisk eksempel, hvor det er nødvendigt at benytte køretidsrefleksion. Det er kun på køretid, at det giver mening at browse metoder, for inden da er der ikke dannet nogle objekter, og metoderne er ikke blevet kaldt. 24
25 Figur 8: Der dannes et hieraki af skyggeklasser (metaobjekttårn), hvis der tilknyttes flere metaobjekter til samme baseobjekt. Baseobjekt A er en instans af baseklassen C. C er den skyggeklasse der dannes, når metaobjekt1 tilknyttes A. C er den skyggeklasse der dannes, når metaobjekt2 tilknyttes A. Hver skyggeklasse har en reference til det metaobjekt, der var skyld i dannelsen af skyggeklassen. Frit efter [11]. 6.4 Performance og pladsforbrug Metaobjekter kan tilknyttes baseobjekter dynamisk på køretidspunktet. MetaJava er forsøgt implementeret således, at systemet ikke vil medføre noget overhead, med mindre man rent faktisk benytter meta-funktionaliteten. Benytter man sig af opførselsrefleksion i MetaJava er det dog klart, at pladsforbruget vil blive forøget. Hver gang et metaobjekt tilknyttes et baseobjekt, bliver der dannet en skyggeklasse, hvilket optager plads. Der er dog i MetaJava sørget for, at metaobjekter og skyggeklasser bliver fjernet af garbage collectoren, hvis der ikke længere findes referencer til dem. MetaJava bygger på en udvidelse af Java s virtuelle maskine, og MetaJavaapplikationer vil ikke performe dårligere på hverken kompileringstid eller loadtid, end almindelige Java-applikationer. Derimod vil man opleve langsommere køretider, hvis man benytter sig af opførselsrefleksion i MetaJava. Der er ikke foretaget egentlige performance-optimeringer i MetaJava. Skulle man forbedre performance, kunne man f.eks. have lavet en Just-in-time (JIT) kompiler til Meta- Java. 25
26 6.5 Opsamling på MetaJava s refleksion I MetaJava er det muligt at benytte introspektiv opførselsrefleksion og strukturel refleksion på køretidspunktet. Disse refleksive egenskaber stammer fra Java. Derudover understøtter MetaJava manipulerende opførselsrefleksion på køretidspunktet. 7 Refleksion i OpenJava OpenJava er en udvidelse af Java, som gør det muligt at benytte strukturel refleksion og opførselsrefleksion til at lave ændringer og udvidelser af en applikation. I OpenJava foregår refleksion på kompileringstidspunktet, hvilket betyder, at det har været nødvendigt at udvikle en ny kompiler. Til gengæld kan programmer udviklet med OpenJava køres på Java s egen virtuelle maskine som en normal Javaapplikation. 7.1 Design af OpenJava s MOP Ligesom MetaJava benytter OpenJava sig af et metaniveauprogram, som foretager de refleksive ændringer i applikationsprogrammet (se afsnit 6). På metaniveauet specificeres altså hvilke ændringer, der skal foretages på baseniveauet. For at kunne understøtte refleksion på kompileringstidspunktet har det været nødvendigt at udvikle en ny kompiler; ojc (jvf. figur 9). OpenJava-kompileren benytter sig af metaniveauprogrammet til at oversætte OpenJava-kildekode til almindelig Java-kildekode. Det færdige produkt af en kompilering er eksekverbar bytekode, som kan køres på Java s virtuelle maskine (JVM). Figur 9: Overblik over OpenJava s kompiler. Frit efter [3]. 26
27 Selve kompileren består af to moduler; et oversættermodul og et Java kompilermodul [3]. Sidstnævnte er blot Java s almindelige kompiler, der kompilerer Java-kode til bytekode, så denne vil vi ikke omtale nærmere. Figur 10: Oversigt over kompilermoduler. Frit efter [3]. Den interessante del af OpenJava-kompileren er det førstnævnte modul; oversættermodulet. Dette modul bruger kildekoden og metaniveauprogrammet til at generere almindelig Java-kildekode. Dette gøres på følgende måde: På baggrund af OpenJava-kildekode, og de tilknyttede metaobjekter, skabes der et abstrakt syntakstræ. Herefter skabes der, i overensstemmelse med de transformationer, der er specificeret i metaobjekterne, et nyt abstrakt syntakstræ. Endeligt genereres den almindelige Java-kildekode ud fra sidstnævnte abstrakte syntakstræ [3]. Figur 11: OpenJava s oversætter. Frit efter [3] Oversættelse og metaobjekter Når en baseklasse skal oversættes sker det på baggrund af definitioner specificeret i metaniveauet. Alle klasser fra baseniveauet har et metaobjekt tilknyttet. Dette metaobjekt har ansvaret for oversættelsen af baseklassen. Ved at benytte nøgleordet instantiates angiver hvilken metaklasse, der er ansvarlig for oversættelse af baseklassen. Dette gøres ved brug af notationen: class A instantiates MetaA 27
28 hvor A er baseklassen og MetaA er metaklassen. Et objekt af MetaA har derved ansvaret for oversættelsen af A [3]. Oversættelsen af baseklasser sker i følgende tre trin [13]: 1. OpenJava analyserer kildekoden og genererer et metaobjekt for hver baseklasse. 2. Metoderne i metaobjekterne kaldes, hvilket betyder, at koden for baseklassen ændres i henhold til definitioner specificeret af metaobjekterne. Dette betyder, at der genereres almindelig Java-kode, som afspejler ændringer foretaget af metaobjekterne. 3. Ved brug af den almindelige Java-kompiler genereres der Java-bytekode. Ud over at danne Java-kildekode for de oversatte klasser, danner oversætteren ligeledes Java-kildekode med information om metaniveauet. Dette indbefatter også hvilken metaklasse, der er tilknyttet en given baseklasse. Derved kan en baseklasses metaklasse bestemmes på et senere tidspunkt. Figur 12: Metaobjekter og oversættelsesprocessen. Frit efter [3] Hvis der ikke er angivet nogen metaklasse for baseklassen, vil metaklassen OJClass default blive benyttet til at danne et metaobjekt til baseklassen. OJClass er defineret til ikke at foretage oversættelser, og en baseklasse forbliver således uændret, hvis OJClass er tilknyttet [3]. En ny metaklasse skal altid nedarve fra OJClass. I den nye metaklasse definerer programmøren hvilke udvidelser og/eller ændringer, der skal udføres på en baseklasse, når den oversættes. Hvordan dette gøres beskrives i afsnit Det er ikke tilladt at lade en klasse blive administreret af flere metaobjekter, dvs. der kan ikke tilknyttes flere metaobjekter til samme baseklasse. Denne begrænsning er valgt, for at undgå de konflikter, der kan opstå, hvis to metaobjekter 28
29 begge har mulighed for at definere den samme baseklasse. Dette betyder dog også, at det i OpenJava ikke er muligt for en baseklasse at have flere syntaksudvidelser samtidig. Derudover gælder det, at det metaobjekt, der er tilknyttet baseklassen, ikke også administrerer nedarvede klasser. Indre klasse og anonyme klasser har ligeledes et metaobjekt tilknyttet. Dette betyder, at alle baseklasser har deres eget metaobjekt tilknyttet API API et i OpenJava minder en del om Java s refleksions-api. OpenJava s centrale klasser er OJClass, OJMethod, OJField og OJConstructor, som repræsenterer metainformation om henholdsvis en baseklasse, dennes konstruktorer, felter og metoder. I OJClass er der defineret en række metoder, som kan bruges til at manipulere baseklassen. Derudover er der defineret en række introspektive metoder, der kan bruges til at hente information om baseklassens opførelse og struktur. Metoderne bruges, når baseklasserne oversættes af OpenJava-kompileren. Oversættelse af baseklassens deklaration Måden hvorpå en baseklasse skal oversættes, defineres ved at overskrive metoden translatedefinition, der arves fra OJClass. OpenJava s kompiler benytter denne metode til at oversætte baseklassen med. I OJClass s definition af translatedefinition foretages der ingen transformation. Programmøren er derfor tvunget til at overskrive metoden for at foretage en oversættelse af baseklassen. Hvordan oversættelse af en baseklasse foretages, er eksemplificeret i eksemplet Hello World. Hello World Dette eksempel belyser de generelle aspekter omkring brug af OpenJava og dets API. Derudover vises hvordan oversættelser af en baseklasse foretages v.h.a. en metaklasse ved at benytte oversættelsesmetoden translate- Defintion. Hvis der ses bort fra instantiates MethodPrinter i koden herunder, er der ingen afvigelser fra et normalt Java-program. public class Hello instantiates MethodPrinter public static void main( String[] args ) hello(); static void hello() System.out.println( "Hello, world."); Vi ønsker nu at modificere programmet Hello, så der i starten af hvert metodekald, foretages en udskrivning til shell en. 29
30 public class Hello public static void main( String[] args ) System.out.println( "main is called." ); hello(); static void hello() System.out.println( "hello is called."); System.out.println( "Hello, world."); I stedet for at ændre i klassen Hello benyttes en metaklasse; MethodPrinter. import openjava.mop.*; import openjava.ptree.*; public class MethodPrinter instantiates Metaclass extends OJClass public void translatedefinition() throws MOPException OJMethod[] methods = getdeclaredmethods(); for (int i = 0; i < methods.length; ++i) Statement printer = makestatement( "System.out.println( \"" + methods[i] + " is called.\" );" ); methods[i].getbody().insertelementat(printer,0); MethodPrinter instantierer metaklassen Metaclass, som findes i OpenJava s pakke openjava.mop. Det eneste Metaclass bruges til, når MethodPrinter oversættes, er, at skabe default konstruktorer og en default translatedefinitionmetode for MethodPrinter. MethodPrinter arver fra OJClass og overskriver selv metoden translatedefinition. translatedefinition er i eksemplet defineret til at ændre en baseklasse, så alle metoder i baseklassen, starter med at udskrive, at de er blevet kaldt. Dette gøres på følgende måde. 1. Metoderne erklæret i baseklassen reificeres med getdeclaredmethods(). Værdien, der returneres af metoden, er et array af OJMethod-metaobjekter. 2. Det returnerede array løbes igennem og for hver metode tilføjes der en System.out.prinln i starten af metoden, som udskriver teksten [methodname] is called. 30
31 Programmet kompileres nu med OpenJava s kompiler. Først kompileres metaklassen og derefter baseklassen. ojc MethodPrinter.oj ojc Hello.oj Produktet af kompileringerne er: MethodPrinter.java, MethodPrinter.class, Hello.java og Hello.class. Resultatet af at køre Hello bliver: void Hello.main(String[]) is called) void Hello.hello() is called Hello, world Oversættelse af kald til objekter m.m. Der kan opstå problemer i forbindelse med oversættelse af en klasse. Dette skyldes, at ændringer af f.eks. en metodes argumenter, uden samtidig ændring af koden det sted metoden bliver kaldt, medfører inkonsistens. OpenJava giver mulighed for at definere de ønskede ændringer i metaklassen, samt indføre disse ændringer det sted, metoden kaldes: public Expression expandmethodcall(methodcall expr, Environment env) Ud over ovenstående metode eksisterer der refleksive metoder til at fjerne metodekald, tilføje metodekald osv. Disse metoder er defineret i OJClass. 7.2 Anvendelse OpenJava har flere forskellige anvendelsesmuligheder. Eksempelvis kan OpenJava benyttes til at implementere designmønstre. Designmønstre I Java eksisterer der ikke et bibliotek, som indeholder designmønstre. Ønsker man at benytte et givent designmønster, er det derfor nødvendigt selv at implementere dette designmønster. En ulempe ved dette er, at det ikke umiddelbart kan ses af en klassedefinition hvilket designmønster, der er brugt. I store programmer kan det hurtigt blive svært at få overblik over kildekoden. Endvidere er det ikke muligt at genbruge et allerede implementeret designmønster i en anden klasse. Dette betyder, at programmøren skal implementere samme designmønster i den nye klasse. I det følgende eksempel skal klassen Vector tilpasses interfacet Stack, og til denne implementation benyttes designmønstret Adapter. Klassen Vector er defineret ved: 31
32 Figur 13: Klassediagram for implementation af adapter-mønstret i Java. public class Vector boolean isempty(); Enumeration elements(); Object lastelement(); void addelement(object o);... Interfacet Stack er defineret ved: public interface Stack boolean isempty(); Enumeration elements(); Object peek(); void push(object o); Object pop(); For at implementere adapter-mønstret i Java benyttes klassen VectorStack. Dette medfører den klassestruktur der er defineret for adapter-mønstret i [10] (jvf. figur 13). public class VectorStack implements Stack private Vector _vector; VectorStack(Vector v)_vector = v; boolean isempty() return _vector.isempty(); Enumeration elements() return _vector.elements(); 32
33 Object peek() return _vector.lastelement(); void push(object o) _vector.addelement(o); Object pop()... Problemet med navngivningen, og derved læseligheden, ses tydeligt i eksemplet. Ved at inkludere designmønstret i klassenavnet, vil man opnå en mere systematisk navngivning. I henhold til dette skal klassen VectorStack i stedet navngives VectorStackAdapter. OpenJava giver mulighed for at implementere adapter-mønstret som en metaklasse. Som det ses af ovenstående eksempel, benyttes adapter-mønstret blot til at videredelegere metodekald. En implementering af metaklassen Adapter- Pattern giver mulighed for at foretage denne videredelegering vha. syntaksudvidelse: public class VectorStack instantiates AdapterPattern adapts Vector in _vector to Stack Object peek() forwards lastelement; void push(object o) forwards addelement; Object pop()... Ved at skrive VectorStack instantiates AdapterPattern tydeliggøres det, at VectorStack implementerer AdapterPattern. adapts Vector in _vector to Stack specificerer, at Vector tilpasses interfacet Stack. I de metoder hvor nøgleordet forwards indgår, videredelegeres metodekaldet. Er der navnesammenfald mellem metoder defineret i henholdsvis Stack og Vector håndteres videredelegeringen af AdapterPattern. Design-by-Contract En anden anvendelsesmulighed af OpenJava er at understøtte Design-by-Contract. OpenJava giver mulighed for at lave den nødvendige sprogudvidelse, der betyder, at post- og præbetingelser kan implementeres (jvf. 4-ugers projekt udarbejdet af Kasper Bøgebjerg Petersens F2001). 7.3 Opsamling på OpenJava s refleksion Det er i OpenJava muligt at benytte introspektiv opførsels- og strukturel refleksion på kompileringstidspunktet. Endvidere understøtter OpenJava manipulerende opførselsrefleksion på kompileringstidspunktet, og denne refleksionsform kan desuden simuleres på køretidspunktet. Endeligt kan man i OpenJava benytte manipulerende strukturel refleksion på kompileringstidspunktet. 33
34 8 Refleksion i Javassist 8.1 Design Javassist er en udvidelse af Java s standard refleksions-api, der gør det muligt at benytte fuld strukturel refleksion. Javassist giver altså et program mulighed for både at se og ændre definitionerne af dets egne klasser og metoder. Javassist understøtter strukturel refleksion uden at ændre Java s kompiler og køretidssystem. I stedet benytter Javassist et klassebibliotek, der sætter det i stand til at udføre strukturel refleksion på en klasse ved at ændre klassens bytekode, når denne bliver loadet ind i JVM en. For at kunne ændre bytekoden på load-time skal man som bruger af Javassist skrive sin egen klasse-loader, der nedarver fra Javas indbyggede klasse, ClassLoader. Når bytekoden for en klasse, der er repræsenteret ved en.class-fil, loades med brugerens egen klasse-loader, er det i klasse-loaderen muligt at bruge Javassists klassebibliotek til at se og ændre definitionen for klassen, inden den loades ind i JVM en. Efter at eventuelle ændringer er foretaget skal klasse-loaderen sørge for at loade den modificerede.class-fil ind i den almindelige JVM, og programmet kan derefter køre videre [4]. Udviklerne af Javassist har overordnet set haft tre krav til systemets design: 1. Source level abstraktion for brugere af Javassist, hvilket betyder, at man ikke behøver at have viden om Java s bytekode for at benytte Javassist. 2. Javassist skal udføre strukturel refleksion så effektivt som muligt. 3. Javassist skal understøtte en typesikker strukturel refleksion. Vi vil i det følgende gennmgå, hvordan Javassist er implementeret og hvordan designkravene er forsøgt opfyldt. 8.2 Implementation Den primære klasse i Javassist er CtClass, der repræsenterer bytekoden for en klasse. Når en klasse bliver loadet af brugerens egen klasse-loader, opretter man således en instans af CtClass for at gøre klassen tilgængelig for introspektion og manipulation. Mulighederne for introspektion ved brug af CtClass er stort set identiske med Java s standard refleksions-api. Dog er det i Javassist ikke muligt at oprette instanser eller udføre metodekald, da disse ikke giver mening på load-time. Javassist tillader at den brugerdefinerede klasse-loader definerer en helt ny tom klasse, uden felter og metoder. Dette gøres ved at oprette en ny instans af CtClass: CtClass c = new CtNewClass(); 34
35 Herved fås en helt tom klasse, og man kan så efterfølgende tilføje passende felter og metoder. CtClass har en lang række metoder til modifikation af klassedefinitioner. Eksempelvis: void bepublic() gør klassen public. void setname(string name) ændrer klassens navn og udskifter alle forekomster af det gamle navn i klassens metoder med det nye navn. void addmethod(ctmethod m, String name, ClassMap map) indsætter en ny metode i klassen. CtMethod er en klasse i Javassists klassebibliotek, der repræsentere bytekoden for en metode. Når metoden m kopieres kan man i hashtabellen map specificere, hvilke klassenavne i metodekroppen der skal udskriftes. En instans af klassen CtMethod, kan fås ved at benytte den introspektive metode getdeclaredmethods(). Som det fremgår af sidstenævnte metode, tillader Javassist at man kopiere en allerede kompileret metodekrop fra en klasse til en anden, når der skal oprettes en ny metode. Herved opnås, at brugeren ikke skal modificere bytekode, hvilket opfylder det første designkrav. Man kunne i stedet have valgt at bruge kildekode til at specificere en ny metodes krop. Det ville dog betyde, at denne kode skulle kompileres løbende, hvilket ville give dårlig performance. For at leve op til det andet designkrav har man derfor valgt ikke at implementere denne løsning i Javassist. For at opfylde designkrav nr. 3, tillader Javassist kun begrænsede ændringer af klassedefinitioner. Ved at have følgende restriktioner forhindrer Javassist, at programmer producerer klasser med typefejl: Man kan ikke fjerne metoder eller felter. Det kan give køretidsfejl, hvis man fjerner felter eller metoder fra en klass,e og der er metoder i andre klasser, der tilgår disse. En ny superklasse skal nedarve fra den originale superklasse, da der kan være metoder i andre klasser, der implicit caster den modificerede klasse til den originale superklasse. Man kan ikke ændre en metodes parametre, men kan i stedet tilføje en ny metode med samme navn, men med andre parametre. 8.3 Anvendelse Opførselsrefleksion Som et eksempel på anvendelsen af Javassist har systemets udviklere valgt at implementere opførselsrefleksion. Ideen i Javassists implementation af opførselsrefleksion er den samme som i MetaJava, nemlig at knytte metaobjekter til baseobjekter og så dirigere metodekald på baseobjektet videre til det 35
36 tilknyttede metaobjekt. Modsat MetaJava foregår tilknytningen af metaobjektet dog ikke på køretidspunktet, men på load-tidspunktet. 8.4 Opsamling på Javassist s refleksion Javassist understøtter både introspektiv og manipulerende opførselsrefleksion samt strukturel refleksion på load-tidspunktet. Det er nemlig både muligt at undersøge og ændre opførsel samt den underliggende datastruktur med dette programmeringssprog. Javassist understøtter samtidig følgende refleksionsmuligheder på køretidspunktet: introspektiv opførsels- og strukturel refleksion samt manipulerende opførselsrefleksion. 9 Diskussion Efter at have gennemgået de enkelte programmeringssprog, har vi været i stand til at udfylde skema 3. Det er udfyldt på baggrund af de opsamlingsafsnit, der er under gennemgangen af hvert sprog. Skemaet vil danne udgangspunkt for den første del af vores diskussion. Vi vil benytte skemaet til at afdække eventuelle sammenhænge mellem de respektive refleksionsformer. Derefter diskuteres fordele og ulemper ved de gennemgåede sprogs refleksionsformer. Afslutningsvis vil vi analysere hvilke konsekvenser hvert sprog har for performance. Refleksion Kompileringstid Load-tid Køretid Introspektion Opførsel OpenJava Javassist Java, MetaJava, OpenJava, Javassist Strukturel OpenJava Javassist Java, MetaJava, OpenJava, Javassist Manipulation Opførsel OpenJava Javassist MetaJava (Javassist, Java1.3, OpenJava) Strukturel OpenJava Javassist Ingen af de fire sprog Tabel 3: Systematisering af de gennemgåede programmeringssprog på baggrund af refleksionsbegreberne. 9.1 Analyse af skema Overordnet set har vi gennem dette projekt fundet en sammenhæng mellem refleksionsbegreberne introspektion og manipulation; når et sprog understøtter manipulation må det nødvendigvis også understøtte introspektion. Hvis man kan ændre 36
37 et programs struktur eller opførsel, kan man også undersøge disse egenskaber. Ovenstående skema understøtter denne observation. Af skemaet ses, at opførselsrefleksion ikke implicerer strukturel refleksion. Der er alstå ingen umiddelbar sammenhæng mellem muligheden for at kunne undersøge/ændre et programs opførsel, og muligheden for at kunne undersøge/ændre et programs underliggende datastruktur. Alene på baggrund af skemaet kan vi ikke afvise, at strukturel refleksion medfører opførselsrefleksion, men vi er ikke stødt på noget, der antydede dette i vores litteraturstudium. Som det ses af skemaet er samtlige felter udfyldt undtagen ét; nemlig feltet, der svarer til manipulerende strukturel refleksion på køretidspunktet. Der er med andre ord ingen af de fire sprog der understøtter denne refleksionsform. Det er muligt, at man ved at udvide MetaJava kan understøtte dette, ved at tillade metaobjekter at tilføje nye metoder, eller på anden vis ændre klassedefinitionen, på køretidspunktet. Hvis man giver mulighed for at ændre et programs datastruktur på køretidspunktet, vil det samtidig være sværere at sikre programmets konsistens. I Javassist kan man simulere manipulerende opførselsrefleksion på køretidspunktet ved at danne metaobjekter og indsætte hooks i programmet. Man kunne måske forestille sig, at det ligeledes var muligt, at simulere manipulerende strukturel refleksion på køretidspunktet i Javassist. Som Javassist er opbygget, mener vi dog ikke, at dette ville kunne lade sig gøre, eftersom man kun kan foretage strukturelle ændringer gennem klasse-loaderen, dvs. på load-tidspunktet. Vi mener heller ikke, at OpenJava er en mulig kandidat til at simulere manipulerende strukturel refleksion på køretidspunktet, da strukturelle ændringer foretaget vha. dette sprog foregår på kompileringstidspunktet. 9.2 Sammenligning af de enkelte sprog MetaJava understøtter opførselsrefleksion på køretidspunktet, og har samme introspektive strukturelle refleksion som Java (jvf. afsnit 6). Java understøtter en begrænset form for opførselsrefleksion, idet dynamiske proxyklasser giver mulighed for at kunne ændre en metodes opførsel på køretidspunktet (jvf. afsnit 4). Disse dynamiske proxyklasser svarer til MetaJavas skyggeklasser, hvilket betyder at Java1.3 opnår samme refleksive egenskaber som MetaJava. Java1.3 er udviklet efter MetaJava, hvilket kunne indikere, at Sun er blevet inspireret af MetaJava s måde at håndtere opførselsrefleksion på? OpenJava understøtter både manipulerende strukturel refleksion og opførselsrefleksion på kompileringstidspunktet. Eftersom OpenJava er et kompiletidssystem er der mulighed for at foretage syntaksudvidelser. Syntaksudvidelser kan eksempelvis benyttes til at understøtte designmønstre og design-by-contract. Ingen af de øvrige tre programmeringssprog giver mulighed for syntaksudvidelser, da deres refleksionsmekanisme først sættes i gang efter kompileringstidspunktet. 37
38 I OpenJava er det ikke tilladt, at tilknytte flere metaobjekter til samme baseklasse. Dette betyder, at det ikke er muligt at implementere flere designmønstre i samme baseklasse. Det er heller ikke muligt både at implementere et designmønster og design-by-contract i samme klasse. Denne begrænsning kunne eventuelt håndteres ved at implementere designmønstret composite således at én metaklasse kan indeholde flere metaklasser. Da OpenJava er baseret på kildekode-transformation, er kildekoden nødvendig for at benytte systemet. Dette kan være uhensigtsmæssigt i henhold til det objektorienterede paradigme hvor indkapsling og genbrug af kode er essentielt. Ved benyttelse af Javassist kan man i modsætning til OpenJava nøjes med at inkludere et klassebibliotek. Endvidere er det, i modsætning til OpenJava, ikke nødvendigt med selve kildekoden for at kunne køre et Javassist-program; her vil bytekode være tilstrækkeligt. Som det eneste af de undersøgte programmeringssprog, stiller MetaJava et interface til rådighed, der åbner mulighed for at kunne udvide den virtuelle maskine. Dette er dog en egenskab der efter vores mening bør benyttes med omtanke, hvis man skal programmets konsistens. 9.3 Analyse af performance OpenJava understøtter refleksion på kompileringstidspunktet, hvilket betyder at metaobjekter allerede skal tilknyttes baseobjekter på kompileringstidspunktet. Denne tidlige tilknytning vil ikke medføre et overhead på kørselstidspunktet og vil derfor ikke betyde noget for den virtuelle maskines performance. Til gengæld vil den tidlige tilknytning ske på bekostning af programmets flexibilitet. Det vil samtidig betyde, at kompileringstiden af programmet bliver væsentlig forøget, da OpenJava-kode skal igennem to kompilere. Dette kan blive et irritationsmoment ved udvikling af store systemer. Ved at udvikle en ny OpenJava-kompiler, hvor OpenJava-kildekode blev kompileret direkte til bytekode, ville kompileringstiden formentlig blive mindre. MetaJava benytter køretidsrefleksion, hvilket i modsætning til OpenJava, giver en høj fleksibilitet men et vist overhead, idet man kan vente med at tilknytte metaobjekter til baseobjekter på køretidspunktet. Da både MetaJava og Javassist benytter en standard Java-kompiler, er der ikke noget ekstra overhead ved kompileringen af sådanne programmer. Javassist må nødvendigvis, ligesom MetaJava, have et vist overhead på køretidspunktet. 38
39 10 Konklusion Vi har gennem dette projekt fået en god indsigt i begrebet refleksion. Vi har stiftet bekendtskab med fire programmeringssprog, der håndterer refleksion på forskellig vis, og vi har set eksempler på, hvad refleksion kan anvendes til. Gennem systematisering af refleksionsbegreberne samt en analyse af de refleksive egenskaber for de gennemgåede programmeringssprog har vi fået et overblik over begrebet refleksion, dels fået indblik i nogle af de sammenhænge, der eksisterer mellem de forskellige refleksionsformer. Grundet tidsrammen på 4 uger, er det er dog et forholdsvis overfladisk kendskab vi har fået til selve implementationen af de enkelte programmeringssprog. Det har være svært at få en god forståelse for hvordan systemerne virker udelukkende ud fra artikler, bla. fordi litteraturen på området er temmelig begrænset. Gennem praktisk erfaring mener vi, at vi kunne have opnået en bedre forståelse af de enkelte sprog. Litteratur [1] M. Golm. Design and implementation of a meta architecture for Java. Master s thesis, Universität Erlangen-Nürnberg IMDDV IV, [2] J. Malenfant, M. Jacques, and F.-N. Demers. A tutorial on behavioral reflection and its implementation. Reflection 96, San Franscisco, [3] M. Tatsubori. An extension mechanism for the Java language. Master s thesis, University of Tsukuba, [4] S. Chiba. Load-time structural reflection in Java. ECOOP 2000, pages , [5] J. Kleinöder and M. Golm. Transparent and adaptable object replication using a reflective Java. Technical Report TR , Universität Erlangen- Nürnberg IMDDV IV, [6] J. Kleinöder and M. Golm. Metajava: An efficient run-time meta architecture for Java. Proc. IWOOOS 96, pages 54 61, [7] Sun Microsystems. Java 2 SDK Documentation, version [8] Sun Microsystems. The Java Tutorial, The Reflection API. 39
40 [9] S. Halloway. Using dynamic proxies to layer new functionality over existing code. [10] Ralph Johnson Erich Gamma, Richard Helm and John Vlissides. Design Patterns - Elements of Reusable Object-Oriented Software. Addison Weslay, [11] M. Golm and J. Kleinöder. Metajava- a platform for adaptable operatingsystem mechanisms. Technical Report TR , Universität Erlangen- Nürnberg IMDDV IV, [12] M. Golm and J. Kleinöder. metaxa and the future of reflection. OOPSLA 98 Workshop, Vancouver, Canada, [13] M. Tatsubori, S. Chiba, M.-O. Killijan, and K. Itano. Openjava: A classbased macro system for Java. Reflection and software engineering,
41 A Appendix: Klassediagrammer Figur 14: Klassediagram for pakken app.browser Figur 15: Klassediagram for pakken app.gui 41
42 B Appendix: Kildekode B.1 Pakken app.browser package app.browser; An object of this class contains all the information extracted from a class. The information is extracted in the constructor. import java.lang.reflect.field; import java.lang.reflect.method; import java.lang.reflect.constructor; import java.lang.reflect.modifier; public class ClassInfo private String _classname; private StaticFieldInfo[] _staticfields; private MethodInfo[] _methods; private ConstructorInfo[] _constructors; private Class _superclass; private Class[] _interfaces; private String[] _interfacenames; private String _modifiers; private String _packagename; public ClassInfo(Class c) extractclassname(c); extractmodifiers(c); extractstaticfields(c); extractmethods(c); extractconstructors(c); extractsuperclass(c); extractinterfaces(c); extractpackagename(c); Method is called by constructor to extract the class name from c The Class object to extract name from. private void extractclassname(class c) _classname = getclassname(c); 42
43 Method is called by constructor to extract the static fields from c The Class object to extract fields from. private void extractstaticfields(class c) Field[] fields = c.getfields(); _staticfields = new StaticFieldInfo[fields.length]; for(int i = 0; i<fields.length; i++) _staticfields[i] = new StaticFieldInfo(fields[i]); Method is called by constructor to extract the methods from c The Class object to extract methods from. private void extractmethods(class c) Method[] methods = c.getmethods(); _methods = new MethodInfo[methods.length]; for(int i = 0; i<methods.length; i++) _methods[i] = new MethodInfo(methods[i]); Method is called by constructor to extract the c The Class object to extract constructors from. private void extractconstructors(class c) Constructor[] constructors = c.getconstructors(); _constructors = new ConstructorInfo[constructors.length]; for(int i = 0; i<constructors.length; i++) _constructors[i] = new ConstructorInfo(constructors[i]); Method is called by constructor to extract the super class to c The Class object to extract super class from. private void extractsuperclass(class c) _superclass = c.getsuperclass(); 43
44 Method is called by constructor to extract the interfaces from Class c The Class object to extract interfaces from. private void extractinterfaces(class c) _interfaces = c.getinterfaces(); _interfacenames = new String[_interfaces.length]; for(int i = 0; i<_interfaces.length; i++) _interfacenames[i] = getclassname(_interfaces[i]); protected void extractmodifiers(class c) _modifiers = Modifier.toString(c.getModifiers()); if (_modifiers.equals( null)) _modifiers = ; private void extractpackagename(class c) // For some reason package names belonging to objects of this application is omitted and null is returned. // This problem is a result of the classloader. Package p = c.getpackage(); if(p null) _packagename = p.getname(); else _packagename = null; //System.out.println(_packageName + " " + getclassname()); Method returns class String representing class name. public String getclassname() return _classname; Method returns static Fields of Array of FieldInfo objects representing the fields in the class. public StaticFieldInfo[] getstaticfields() 44
45 return _staticfields; Method returns the methods of Returns an array of MethodInfo objects representing the methods in the class. public MethodInfo[] getmethods() return _methods; Method returns the contructors to the Returns an array of ConstructorInfo objects representing the contructors to the class. public ConstructorInfo[] getconstructors() return _constructors; Method returns the super class to the Returns a String representing the super class. public Class getsuperclass() return _superclass; Method returns the contructors to the Returns an array of strings representing the interfaces to the class. public Class[] getinterfaces() return _interfaces; public String[] getinterfacenames() return _interfacenames; 45
46 public String getmodifiers() return _modifiers; public String getpackagename() return _packagename; public boolean hassuperclass() return (_superclass null); public boolean hasinterfaces() return (_interfaces.length 0); public boolean isinpackage() return _packagename null; public static String getclassname(class c) int index = c.getname().lastindexof(.); if(index == -1) return c.getname(); return c.getname().substring(index + 1); public String tostring() String temp; temp = Class: +getclassname()+\n\n; temp += getpackagename() + \n; if(getmodifiers() == null) temp += Class: +getclassname()+\n; else temp += + getmodifiers() + Class: +getclassname()+\n; String[] interfaces = getinterfacenames(); 46
47 if(interfaces.length 0) temp += implements ; for(int i = 0; i<interfaces.length; i++) temp += +interfaces[i] ; if(i<(interfaces.length-1)) temp +=, ; temp += \n; if(hassuperclass()) temp += extends +getsuperclass().getname(); temp+= \n; ConstructorInfo[] constructors = getconstructors(); temp += Constructors: \n; for(int i = 0; i<constructors.length; i++) temp += + constructors[i] + \n; StaticFieldInfo[] fields = getstaticfields(); for(int i = 0; i<fields.length; i++) temp += + fields[i] + \n; temp += Methods: \n; MethodInfo[] methods = getmethods(); for(int i = 0; i<methods.length; i++) temp += + methods[i] + \n; return temp; package app.browser; An object of this class contains information about the state of an object. The information is extracted in the constructor. import java.lang.reflect.method; import java.lang.reflect.field; import java.util.arraylist; public class ObjectInfo 47
48 private FieldInfo[] _fieldinfo; private String _objecttype; private Class _objecttypeclass; public ObjectInfo(Object o) extractobjecttype(o); extractfieldinfo(o); Method is called by constructor to extract the object type from o the object to extract type from. protected void extractobjecttype(object o) _objecttypeclass = o.getclass(); _objecttype = _objecttypeclass.getname(); Method is called by constructor to extract information about the o the object to extract the fields from. protected void extractfieldinfo(object o) Class c = o.getclass(); Field[] fields = c.getfields(); _fieldinfo = new FieldInfo[fields.length]; for (int i = 0; i < fields.length; i++) _fieldinfo[i] = new FieldInfo(fields[i], o); public FieldInfo[] getfieldinfo() return _fieldinfo; public String getobjecttype() return _objecttype; 48
49 public Class getobjecttypeclass() return _objecttypeclass; public String tostring() String temp = Objecttype: + getobjecttype() + \n + Fields \n; for (int i=0; i < _fieldinfo.length; i++) temp = temp + _fieldinfo[i] + \n; return temp; package app.browser; An object of this class contains information about the methods of a class. The information is extracted in the constructor. import java.lang.reflect.method; import java.lang.reflect.modifier; import java.util.arraylist; public class MethodInfo private String _methodname; private String[] _parameters; private String _modifiers; private String _returntype; public MethodInfo(Method m) extractmethodname(m); extractparameters(m); extractmodifiers(m); extractreturntype(m); Method is called by constructor to extract the method name from Method. 49
50 @param m The method object to extract name from. protected void extractmethodname(method m) _methodname = m.getname(); Method is called by constructor to extract the parameters from Method and converts to m The method object to parameters from. protected void extractparameters(method m) Class[] parametertypes = m.getparametertypes(); _parameters = new String[parameterTypes.length]; for(int i = 0; i < parametertypes.length; i++) _parameters[i] = parametertypes[i].getname(); Method is called by constructor to extract the modifiers from m The method object to parameters from. protected void extractmodifiers(method m) _modifiers = Modifier.toString(m.getModifiers()); Method is called by constructor to extract the return type from the Method m The method object to parameters from. public void extractreturntype(method m) _returntype = m.getreturntype().getname(); Method returns the method name as a returns String object representing the method name public String getmethodname() 50
51 return _methodname; Method returns the parameter types needed to invoke the Returns an array containing Strings representing the parameters public String[] getparameters() return _parameters; Method returns the modifiers to the Returns a string representing the modifiers public String getmodifiers() return _modifiers; Method returns the return Returns a string representing the return type public String getreturntype() return _returntype; public String tostring() String temp = + getmodifiers() + + getreturntype()++ getmethodname() + (; String[] parameters = getparameters(); for(int i = 0; i<parameters.length; i++) temp += parameters[i]; if(i<(parameters.length-1)) temp+=, ; temp += ); return temp; package app.browser; 51
52 An object of this class contains information about the constructors of a class. The information is extracted in the constructor. import java.lang.reflect.constructor; import java.lang.reflect.modifier; public class ConstructorInfo private String _constructorname; private String[] _parameters; private String _modifiers; public ConstructorInfo(Constructor m) extractconstructorname(m); extractparameters(m); extractmodifiers(m); Method is called by constructor to extract the method name from m The method object to extract name from. private void extractconstructorname(constructor c) _constructorname = c.getname(); Method is called by constructor to extract the parameters from Method and converts to m The method object to parameters from. private void extractparameters(constructor c) Class[] parametertypes = c.getparametertypes(); _parameters = new String[parameterTypes.length]; for(int i = 0; i < parametertypes.length; i++) _parameters[i] = parametertypes[i].getname(); Method is called by constructor to extract the modifiers from m The method object to parameters from. 52
53 protected void extractmodifiers(constructor c) _modifiers = Modifier.toString(c.getModifiers()); Method returns the method name as a returns String object representing the method name public String getconstructorname() return _constructorname; Method returns the parameter types needed to invoke the Returns an array containing Strings representing the parameters public String[] getparameters() return _parameters; Method returns the modifiers to the Returns a string representing the modifiers public String getmodifiers() return _modifiers; public String tostring() String temp = + getmodifiers() + + getconstructorname() + (; String[] parameters = getparameters(); for(int i = 0; i<parameters.length; i++) temp += parameters[i]; if(i<(parameters.length-1)) temp+=, ; temp += ); return temp; 53
54 package app.browser; An object of this class contains information about the fields of a class. The information is extracted in the constructor. import java.lang.reflect.field; import java.lang.reflect.modifier; public class FieldInfo extends StaticFieldInfo private String _fieldvalue; private Object _fieldvalueobject; public FieldInfo(Field f, Object o) super(f); extractfieldvalue(f, o); Method is called by constructor to extract the value of the field. Necessary to extract the field type if it hasn t been done before by the f The field object to parameters from. protected void extractfieldvalue(field f, Object o) try _fieldvalueobject = f.get(o); _fieldvalue = _fieldvalueobject.tostring(); catch (IllegalArgumentException e1) e1.printstacktrace( System.err ); System.exit( 1 ); catch (IllegalAccessException e2) e2.printstacktrace( System.err ); System.exit( 1 ); public String getfieldvalue() 54
55 return _fieldvalue; public Object getfieldvalueobject() return _fieldvalueobject; public String tostring() return + getmodifiers()++getfieldtype()++getfieldname()+= +getfieldvalue(); package app.browser; An object of this class contains information about the static fields of a class. The information is extracted in the constructor. import java.lang.reflect.field; import java.lang.reflect.modifier; public class StaticFieldInfo private String _fieldname; private String _fieldtype; private String _modifiers; public StaticFieldInfo(Field f) extractmodifiers(f); extractfieldtype(f); extractfieldname(f); Method is called by constructor to extract the field name from the f The field object to extract name from. protected void extractfieldname(field f) _fieldname = f.getname(); 55
56 Method is called by constructor to extract the modifiers from the f The field object to parameters from. protected void extractmodifiers(field f) _modifiers = Modifier.toString(f.getModifiers()); Method is called by constructor to extract the type of the f The field object to parameters from. protected void extractfieldtype(field f) Class typeclass = f.gettype(); _fieldtype = typeclass.getname(); Method is called by constructor to extract the value of the field. Necessary to extract the field type if it hasn t been done before by the f The field object to parameters from. public String getfieldname() return _fieldname; public String getmodifiers() return _modifiers; public String getfieldtype() return _fieldtype; public String tostring() return + getmodifiers()++getfieldtype()++getfieldname(); 56
57 package app.browser; A monitor that is used by GUIBrowser to make sure that the calling program does not continue until the user closes the Swing window. public class WaitMonitor private boolean _open = true; / Makes the calling thread rest until goon() is called public synchronized void rest() while (_open) try wait(); catch(exception e) System.out.println(e); Wakes up waiting threads public synchronized void goon() _open = false; notify(); B.2 Pakken app.gui package app.browser; import app.gui.browserwindow; A static class, that is used to display the object in a Swing frame. It is this class, that the user should use, when he wants to browse a object in his program. public class GUIBrowser 57
58 static boolean _open = true; public static void browse(class c) BrowserWindow browserwindow = BrowserWindow.getInstance(); WaitMonitor waitmonitor = new WaitMonitor(); browserwindow.display(new ClassInfo(c), waitmonitor); waitmonitor.rest(); public static void browse(object o) BrowserWindow browserwindow = BrowserWindow.getInstance(); WaitMonitor waitmonitor = new WaitMonitor(); browserwindow.display(new ObjectInfo(o), waitmonitor); waitmonitor.rest(); package app.gui; import javax.swing.jframe; import javax.swing.jbutton; import javax.swing.jscrollpane; import javax.swing.jtoolbar; import javax.swing.box; import javax.swing.abstractaction; import javax.swing.borderfactory; import javax.swing.imageicon; import app.browser.classinfo; import app.browser.objectinfo; import java.awt.event.windowadapter; import java.awt.event.windowevent; import java.awt.event.actionevent; import java.awt.color; import java.awt.borderlayout; import java.io.file; import app.browser.waitmonitor; BrowserWindow displays the browserwindow. It is a singleton. public class BrowserWindow extends JFrame private static BrowserWindow _browserwindow = null; AbstractAction backaction, forwardaction; JToolBar toolbar; InfoSequence sequence; 58
59 WaitMonitor waitmonitor; private BrowserWindow() setsize(500, 400); setuptoolbar(); sequence = new InfoSequence(); addwindowlistener(new WindowHandler()); Should be called when you want an instance of BrowserWindow. It always returns the same instance of BrowserWindow, since BrowserWindow is a singleton. public static BrowserWindow getinstance() if (_browserwindow == null) _browserwindow = new BrowserWindow(); return _browserwindow; Displays an instance of ClassInfo in the window, and adds the instance to the image queue. The method is called by c the class to be displayed. public void display(classinfo c, WaitMonitor w) displayinfo(c); sequence.add(c); waitmonitor = w; Displays an instance of ObjectInfo in the window, and adds the instance to the image queue. The method is called by o the object to be displayed. public void display(objectinfo o, WaitMonitor w) displayinfo(o); sequence.add(o); waitmonitor = w; 59
60 Displays an instance of ClassInfo in the window, and adds the instance to the image queue. The method is called by c the class to be displayed. public void display(classinfo c) displayinfo(c); sequence.add(c); Displays an instance of ObjectInfo in the window, and adds the instance to the image queue. The method is called by o the object to be displayed. public void display(objectinfo o) displayinfo(o); sequence.add(o); Displays an instance of ClassInfo in the window, and adds the instance to the image queue. The method is called by BackAction and ForwardAction, which are inner classes of c the class to be displayed. protected void displayinfo(classinfo c) ClassInfoCanvas classinfocanvas = new ClassInfoCanvas(c); getcontentpane().removeall(); getcontentpane().add(classinfocanvas, BorderLayout.CENTER); getcontentpane().add(toolbar, BorderLayout.NORTH); getcontentpane().add(new JScrollPane(classInfoCanvas)); setvisible(true); Displays an instance of ObjectInfo in the window, and adds the instance to the image queue. The method is called by BackAction and ForwardAction, which are inner classes of c the class to be displayed. protected void displayinfo(objectinfo o) 60
61 ObjectInfoCanvas objectinfocanvas = new ObjectInfoCanvas(o); getcontentpane().removeall(); getcontentpane().add(objectinfocanvas, BorderLayout.CENTER); getcontentpane().add(toolbar, BorderLayout.NORTH); getcontentpane().add(new JScrollPane(objectInfoCanvas)); setvisible(true); Sets up the Toobar, which has two buttons: back and forward. public void setuptoolbar() toolbar = new JToolBar(); backaction = new BackAction(Back); forwardaction = new ForwardAction(Forward); JButton backbutton = toolbar.add(backaction); JButton forwardbutton = toolbar.add(forwardaction); backbutton.setborder(borderfactory.createraisedbevelborder()); forwardbutton.setborder(borderfactory.createraisedbevelborder()); toolbar.setborder(borderfactory.createcompoundborder( BorderFactory.createLineBorder(Color.darkGray), //outer frame. BorderFactory.createEmptyBorder(2,2,4,2))); //inner framn. toolbar.setfloatable(false); // The toolbar may not be moved. Windowhandler for BrowserWindow. class WindowHandler extends WindowAdapter Is called when the BrowserWindow is closed. It makes the JVM terminate nicely. public void windowclosing(windowevent e) dispose(); waitmonitor.goon(); Action for the back button in the toolbar of BrowserWindow. It has the same function as a back button in a webbrowser like Netscape. class BackAction extends AbstractAction BackAction(String name) super(name); 61
62 String iconfilename = images/ + name +.gif; if (new File(iconFileName).exists()) putvalue(small_icon, new ImageIcon(iconFileName)); public void actionperformed(actionevent e) Object o = sequence.goback(); if (o null) if(o instanceof ClassInfo) BrowserWindow.getInstance().displayInfo((ClassInfo) o); else if (o instanceof app.browser.objectinfo) BrowserWindow.getInstance().displayInfo((ObjectInfo) o); Action for the forward button in the toolbar of BrowserWindow. It has the same function as a forward but in a webbrowser like Netscape. class ForwardAction extends AbstractAction ForwardAction(String name) super(name); String iconfilename = images/ + name +.gif; if (new File(iconFileName).exists()) putvalue(small_icon, new ImageIcon(iconFileName)); public void actionperformed(actionevent e) Object o = sequence.goforward(); if (o null) if(o instanceof ClassInfo) BrowserWindow.getInstance().displayInfo((ClassInfo) o); else if (o instanceof app.browser.objectinfo) BrowserWindow.getInstance().displayInfo((ObjectInfo) o); 62
63 package app.gui; import javax.swing.jpanel; import javax.swing.box; import javax.swing.boxlayout; import java.awt.color; import app.browser.classinfo; import app.browser.staticfieldinfo; import app.browser.constructorinfo; import app.browser.methodinfo; This is the canvas where the information about a class is displayed. It is part of the BrowserWindow. All the information is added to the canvas in the constructor. public class ClassInfoCanvas extends JPanel public ClassInfoCanvas(ClassInfo classinfo) setlayout(new BoxLayout(this,BoxLayout.Y_AXIS)); setbackground(color.white); setinfopanel(classinfo); setlinepanel(); if(classinfo.isinpackage()) setpackagepanel(classinfo); setclasspanel(classinfo); if(classinfo.hassuperclass()) setsuperclasspanel(classinfo); if(classinfo.hasinterfaces()) setinterfacespanel(classinfo); setlinepanel(); StaticFieldInfo[] fieldinfos = classinfo.getstaticfields(); if(fieldinfos.length > 0) setsubject(static Fields:); setfieldpanels(fieldinfos); setlinepanel(); ConstructorInfo[] constructorinfos = classinfo.getconstructors(); if(constructorinfos.length > 0) setsubject(constructors:); setconstructorpanels(constructorinfos); setlinepanel(); MethodInfo[] methodinfos = classinfo.getmethods(); if(methodinfos.length > 0) 63
64 setsubject(methods:); setmethodpanels(methodinfos); private void setinfopanel(classinfo classinfo) RowPanel infopanel = new RowPanel(); BrowserLabel h = new BrowserLabel(class h.setforeground(color.blue); infopanel.add(h); infopanel.add(box.createhorizontalglue()); add(infopanel); + classinfo.getclassname()); private void setpackagepanel(classinfo classinfo) RowPanel packagepanel = new RowPanel(); packagepanel.add(new BrowserLabel(package + classinfo.getpackagename())); packagepanel.add(box.createhorizontalglue()); add(packagepanel); private void setclasspanel(classinfo classinfo) RowPanel classinfopanel = new RowPanel(); classinfopanel.add(new BrowserLabel(classInfo.getModifiers() + )); classinfopanel.add(new BrowserLabel(class )); classinfopanel.add(new BrowserLabel(classInfo.getClassName())); classinfopanel.add(box.createhorizontalglue()); add(classinfopanel); private void setsuperclasspanel(classinfo classinfo) RowPanel superclasspanel = new RowPanel(); superclasspanel.add(new BrowserLabel(extends )); superclasspanel.add(new ClassLinkButton(classInfo.getSuperClass())); superclasspanel.add(box.createhorizontalglue()); add(superclasspanel); private void setinterfacespanel(classinfo classinfo) RowPanel interfacepanel = new RowPanel(); interfacepanel.add(new BrowserLabel(implements )); Class[] interfaces = classinfo.getinterfaces(); for(int i=0; i<interfaces.length; i++) interfacepanel.add(new ClassLinkButton(interfaces[i])); 64
65 interfacepanel.add(box.createhorizontalglue()); add(interfacepanel); private void setfieldpanels(staticfieldinfo[] fieldinfos) for(int i = 0; i<fieldinfos.length; i++) RowPanel fieldpanel = new RowPanel(); fieldpanel.add(new BrowserLabel(fieldInfos[i].getModifiers() + )); fieldpanel.add(new BrowserLabel(fieldInfos[i].getFieldType() + )); fieldpanel.add(new BrowserLabel(fieldInfos[i].getFieldName())); fieldpanel.add(box.createhorizontalglue()); add(fieldpanel); private void setconstructorpanels(constructorinfo[] constructorinfos) for(int i = 0; i<constructorinfos.length; i++) RowPanel constructorpanel = new RowPanel(); constructorpanel.add(new BrowserLabel(constructorInfos[i].getModifiers() + )); constructorpanel.add(new BrowserLabel(constructorInfos[i].getConstructorName())); String[] parameters = constructorinfos[i].getparameters(); constructorpanel.add(new BrowserLabel(()); for(i=0; i< parameters.length; i++) if(i+1 < parameters.length) constructorpanel.add(new BrowserLabel(parameters[i] +,)); else constructorpanel.add(new BrowserLabel(parameters[i])); constructorpanel.add(new BrowserLabel())); constructorpanel.add(box.createhorizontalglue()); add(constructorpanel); private void setmethodpanels(methodinfo[] methodinfos) for(int i = 0; i<methodinfos.length; i++) RowPanel methodpanel = new RowPanel(); methodpanel.add(new BrowserLabel(methodInfos[i].getModifiers() + )); methodpanel.add(new BrowserLabel(methodInfos[i].getReturnType() + )); methodpanel.add(new BrowserLabel(methodInfos[i].getMethodName())); String[] parameters = methodinfos[i].getparameters(); 65
66 ,)); methodpanel.add(new BrowserLabel(()); for(int j=0; j< parameters.length; j++) if(j+1 < parameters.length) methodpanel.add(new BrowserLabel(parameters[j] + else methodpanel.add(new BrowserLabel(parameters[j])); methodpanel.add(new BrowserLabel())); methodpanel.add(box.createhorizontalglue()); add(methodpanel); private void setlinepanel() RowPanel linepanel = new RowPanel(); linepanel.add(new BrowserLabel( )); linepanel.add(box.createhorizontalglue()); add(linepanel); private void setsubject(string s) RowPanel subjectpanel = new RowPanel(); BrowserLabel fieldheading = new BrowserLabel(s); fieldheading.setforeground(color.blue); subjectpanel.add(fieldheading); subjectpanel.add(box.createhorizontalglue()); add(subjectpanel); package app.gui; import javax.swing.jpanel; import java.awt.color; import java.awt.dimension; import app.browser.objectinfo; import app.browser.fieldinfo; import javax.swing.boxlayout; import javax.swing.box; This is the canvas where the information about an object is displayed. It is part of the BrowserWindow. All the information is added to the canvas in the constructor. public class ObjectInfoCanvas extends JPanel 66
67 // Constructor. public ObjectInfoCanvas(ObjectInfo objectinfo) setlayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setbackground(color.white); // Insert heading. RowPanel heading = new RowPanel(); add(heading); BrowserLabel h = new BrowserLabel(Instance of class: ); h.setforeground(color.blue); heading.add(h); heading.add(new ClassLinkButton(objectInfo.getObjectTypeClass())); heading.add(box.createhorizontalglue()); RowPanel space1 = new RowPanel(); add(space1); space1.add(new BrowserLabel( )); space1.add(box.createhorizontalglue()); RowPanel space2 = new RowPanel(); add(space2); BrowserLabel fieldheading = new BrowserLabel(Fields: ); fieldheading.setforeground(color.blue); space2.add(fieldheading); space2.add(box.createhorizontalglue()); // Insert fields. FieldInfo[] fieldinfo = objectinfo.getfieldinfo(); int n = fieldinfo.length; for (int i=0; i < n; i++) RowPanel row = new RowPanel(); add(row); row.add(new BrowserLabel( + fieldinfo[i].getmodifiers() + )); row.add(new BrowserLabel( + fieldinfo[i].getfieldtype() + )); row.add(new BrowserLabel( + fieldinfo[i].getfieldname() + = )); Object c = fieldinfo[i].getfieldvalueobject(); if (myisprimitive(c.getclass())) row.add(new BrowserLabel( + fieldinfo[i].getfieldvalue())); else row.add(new ObjectLinkButton(c)); row.add(box.createhorizontalglue()); private boolean myisprimitive(class c) String name = c.getname(); 67
68 if (name == java.lang.boolean) return true; if (name == java.lang.character) return true; if (name == java.lang.byte) return true; if (name == java.lang.short) return true; if (name == java.lang.integer) return true; if (name == java.lang.long) return true; if (name == java.lang.float) return true; if (name == java.lang.double) return true; if (name == java.lang.void) return true; return false; package app.gui; import javax.swing.jbutton; import java.awt.event.actionlistener; import java.awt.event.actionevent; import java.awt.insets; import java.awt.color; import app.browser.classinfo; Button used for a class link. It contains its own actionlistener. public class ClassLinkButton extends JButton public ClassLinkButton(Class c) super(c.getname().substring(c.getname().lastindexof(.)+1)); initialize(c); private void initialize(class c) setborderpainted(false); setfocuspainted(false); // Deletes the package. // Deletes the border. 68
69 // Removes the border around text, when the button gets focus. setmargin(new Insets(0,0,0,0)); setforeground(new Color(105,9,119)); setbackground(color.white); addactionlistener(new LinkButtonListener(c)); public class LinkButtonListener implements ActionListener private Class _class; public LinkButtonListener(Class c) _class = c; public void actionperformed(actionevent e) BrowserWindow.getInstance().display(new ClassInfo(_class)); package app.gui; import javax.swing.jbutton; import java.awt.event.actionlistener; import java.awt.event.actionevent; import java.awt.insets; import java.awt.color; import app.browser.objectinfo; Button used for a object link. It contains its own actionlistener. public class ObjectLinkButton extends JButton public ObjectLinkButton(Object o) super(o.tostring()); initialize(o); private void initialize(object o) setborderpainted(false); setfocuspainted(false); // Deletes the border. 69
70 // Removes the border around text, when the button gets focus. setforeground(new Color(105,9,119)); setbackground(color.white); setmargin(new Insets(0,0,0,0)); addactionlistener(new ObjectLinkButtonListener(o)); public class ObjectLinkButtonListener implements ActionListener private Object _object; public ObjectLinkButtonListener(Object o) _object = o; public void actionperformed(actionevent e) BrowserWindow.getInstance().display(new ObjectInfo(_object)); package app.gui; import java.awt.color; import javax.swing.jlabel; An specialized version of JLabel, where the foreground text is black. Is used by ClassInfoCanvas and ObjectInfoCanvas. public class BrowserLabel extends JLabel public BrowserLabel(String text) super(text); setforeground(color.black); package app.gui; import javax.swing.jpanel; import javax.swing.boxlayout; 70
71 This class is used as a panel, which functions as a row in a bigger panel. public class RowPanel extends JPanel public RowPanel() setlayout(new BoxLayout(this,BoxLayout.X_AXIS)); setopaque(false); package app.browser; A static class, that is used to display an object in a Shell. It is this class, that the user should use, when he wants to browse a object in his program. public class ShellBrowser public static void browse(object o) ShellPrinter s = new ShellPrinter(); ObjectInfo objectinfo = new ObjectInfo(o); s.display(objectinfo); public static void browse(class c) ShellPrinter s = new ShellPrinter(); ClassInfo classinfo = new ClassInfo(c); s.display(classinfo); package app.browser; This class is used by the static class ShellBrowser to display information about an object in a Shell. public class ShellPrinter public ShellPrinter() 71
72 @param o The object to print in the shell. protected void display(objectinfo o) System.out.println(o.toString()); c The class to print in the shell. protected void display(classinfo c) System.out.println(c.toString()); package app.gui; import java.util.vector; A sequence of objects. The class has methods to go forward and backward in the sequence. public class InfoSequence private Vector _objectinfos; private int _atindex; protected InfoSequence() _objectinfos = new Vector(); _atindex = _objectinfos.size()-1; Checks if it s possible to go backward in the the object at the new position in the sequence. If it is not possible to go back the method returns null. public Object goback() if (cangoback()) 72
73 _atindex--; return _objectinfos.get(_atindex); else return null; Checks if it s possible to go forward in the the object at the new position in the sequence. If it is not possible to go forward the method returns null. public Object goforward() if (cangoforward()) _atindex++; return _objectinfos.get(_atindex); else return null; Adds a new object to the sequence. If the index is in the middle of the sequence, all the objects that are to the right of the index will be deleted. public void add(object o) for(int i = _objectinfos.size()-1; i>_atindex; i--) _objectinfos.remove(i); _objectinfos.add(o); _atindex = _objectinfos.size()-1; Checks if it s possible to go back in the sequence. public boolean cangoback() return _atindex > 0; Checks if it s possible to go forward in the sequence. 73
74 public boolean cangoforward() return _atindex < _objectinfos.size()-1; B.3 Pakken test.browser package test.browser; import junit.framework.test; import junit.framework.testcase; import junit.framework.testsuite; public class BrowserMainTest extends TestCase public BrowserMainTest(String name)super(name); public static Test suite() TestSuite suite = new TestSuite(); suite.addtest(objectinfotest.suite()); suite.addtest(constructorinfotest.suite()); suite.addtest(methodinfotest.suite()); suite.addtest(fieldinfotest.suite()); suite.addtest(staticfieldinfotest.suite()); suite.addtest(classinfotest.suite()); return suite; public static void main(string[] args) String[] testcasename = BrowserMainTest.class.getName() ; junit.textui.testrunner.main(testcasename); package test.browser; import junit.framework.test; import junit.framework.testcase; import junit.framework.testsuite; import java.lang.reflect.method; import java.lang.reflect.constructor; import app.browser.classinfo; import app.browser.staticfieldinfo; 74
75 import app.browser.methodinfo; import app.browser.constructorinfo; import java.util.observer; import java.util.observable; public class ClassInfoTest extends TestCase public ClassInfoTest(String name) super(name); public static class Person implements Observer private String _name; // testextractstaticfields depends on 2 static fields public static int _count = 0 ; public Person(String name) _count++; _name = name; public String getname()return _name; public void setname(string name)_name = name; public void update(observable observable, Object object) private Person _person; private ClassInfo _classinfo; public void setup() _person = new Person(Reflection); _classinfo = new ClassInfo(_person.getClass()); public void teardown() _classinfo = null; _person = null; private void testclassname(class c) 75
76 assertequals(_classinfo.getclassname(),test.browser.classinfotest.person); public void teststaticfields() StaticFieldInfo[] fieldinfos = _classinfo.getstaticfields(); assertequals(fieldinfos.length,1); public void testmethodinfo() MethodInfo[] methods = _classinfo.getmethods(); Method[] objectmethods = (new Object()).getClass().getMethods(); assertequals(methods.length, objectmethods.length+3); public void testconstructors() ConstructorInfo[] constructors = _classinfo.getconstructors(); Constructor[] constructormethods = (new Object()).getClass().getConstructors(); assertequals(constructors.length, constructormethods.length); public void testsuperclass() assert(_classinfo.hassuperclass()); assertequals(_classinfo.getsuperclass().getname(),java.lang.object); public void testinterfaces() assert(_classinfo.hasinterfaces()); assertequals(_classinfo.getinterfaces().length,1); assertequals(classinfo.getclassname(_classinfo.getinterfaces()[0]),_classinfo.getinterfacenames()[0]); //testing returning that false is returned with no interfaces ClassInfo info = new ClassInfo((new Object()).getClass()); assert(!info.hasinterfaces()); public void testmodifiers() 76
77 assertequals(_classinfo.getmodifiers(),public static); public void testpackagename() ClassInfo info = new ClassInfo((new Object()).getClass()); assertequals(info.getpackagename(),java.lang); public static Test suite() TestSuite suite = new TestSuite(ClassInfoTest.class); return suite; public static void main(string[] args) String[] testcasename = ClassInfoTest.class.getName() ; junit.textui.testrunner.main(testcasename); package test.browser; import junit.framework.test; import junit.framework.testcase; import junit.framework.testsuite; import app.browser.objectinfo; import app.browser.fieldinfo; public class ObjectInfoTest extends TestCase public ObjectInfoTest(String name) super(name); public void testextractobjecttype() Person p = new Person(Test); ObjectInfo o = new ObjectInfo(p); assertequals(o.getobjecttype(), test.browser.person); 77
78 + public void testextractfieldinfo() Person p = new Person(Test); ObjectInfo o = new ObjectInfo(p); FieldInfo[] f = o.getfieldinfo(); assertequals(f[0].getfieldname(), _name); assertequals(f[0].getfieldvalue(), Test); public void testtostring() Person p = new Person(Test); ObjectInfo o = new ObjectInfo(p); assertequals(o.tostring(), Objecttype: test.browser.person\nfields \n _name = Test\n + = 55\n); public static Test suite() TestSuite suite = new TestSuite(ObjectInfoTest.class); return suite; public static void main(string[] args) String[] testcasename = ObjectInfoTest.class.getName() ; junit.textui.testrunner.main(testcasename); package test.browser; import junit.framework.test; import junit.framework.testcase; import junit.framework.testsuite; import app.browser.constructorinfo; import java.lang.reflect.constructor; public class ConstructorInfoTest extends TestCase public ConstructorInfoTest(String name) public java.lang.string public int _age = 14\n + public static int _what 78
79 super(name); public void testconstructorname() Person p = new Person(Test); Class cl = p.getclass(); Constructor[] constructors = cl.getconstructors(); ConstructorInfo c = new ConstructorInfo(constructors[0]); assertequals(constructors.length, 1); assertequals(c.getconstructorname(), test.browser.person); public void testparameters() Person p = new Person(Test); Class cl = p.getclass(); Constructor[] constructor = cl.getconstructors(); ConstructorInfo c = new ConstructorInfo(constructor[0]); String[] parameters = c.getparameters(); assertequals(parameters.length, 1); assertequals(parameters[0], java.lang.string); public void testmodifiers() Person p = new Person(Test); Class cl = p.getclass(); Constructor[] constructor = cl.getconstructors(); ConstructorInfo c = new ConstructorInfo(constructor[0]); // Person only has one constructor. assertequals(c.getmodifiers(), public); public static Test suite() TestSuite suite = new TestSuite(ConstructorInfoTest.class); return suite; public static void main(string[] args) String[] testcasename = ConstructorInfoTest.class.getName() ; junit.textui.testrunner.main(testcasename); 79
80 package test.browser; import junit.framework.test; import junit.framework.testcase; import junit.framework.testsuite; import app.browser.objectinfo; import app.browser.fieldinfo; import java.lang.reflect.field; public class FieldInfoTest extends TestCase public FieldInfoTest(String name) super(name); public void testfieldvalue() Person p = new Person(TestPerson); Class c = p.getclass(); Field[] personfields = c.getfields(); FieldInfo f = new FieldInfo(personFields[0], p); assertequals(f.getfieldvalue(), TestPerson); f = new FieldInfo(personFields[1], p); assertequals(f.getfieldvalue(), 14); f = new FieldInfo(personFields[2], p); assertequals(f.getfieldvalue(), 55); public void testfieldvalueobject() Person p = new Person(TestPerson); Class c = p.getclass(); Field[] personfields = c.getfields(); Field personfield = personfields[2]; FieldInfo f = new FieldInfo(personField, p); Object o = f.getfieldvalueobject(); assert(o instanceof Integer); assertequals(((integer)o).intvalue(), 55); public void testtostring() Person p = new Person(TestPerson); Class c = p.getclass(); Field[] personfields = c.getfields(); Field personfield = personfields[0]; 80
81 FieldInfo f = new FieldInfo(personField, p); assertequals(f.tostring(), public java.lang.string _name = TestPerson); public static Test suite() TestSuite suite = new TestSuite(FieldInfoTest.class); return suite; public static void main(string[] args) String[] testcasename = FieldInfoTest.class.getName() ; junit.textui.testrunner.main(testcasename); package test.browser; import junit.framework.test; import junit.framework.testcase; import junit.framework.testsuite; import app.browser.methodinfo; import java.lang.reflect.constructor; import java.lang.reflect.method; public class MethodInfoTest extends TestCase public MethodInfoTest(String name) super(name); public void testmethodname() Person p = new Person(Test); Class cl = p.getclass(); Method[] methods = cl.getmethods(); MethodInfo c; boolean methodexist = false; for (int i = 0; i < methods.length; i++) c = new MethodInfo(methods[i]); if (c.getmethodname().equals(getname)) methodexist = true; 81
82 assert(methodexist); public void testparameters() Person p = new Person(Test); Class cl = p.getclass(); Method[] methods = cl.getmethods(); MethodInfo c; boolean methodexist = false; for (int i = 0; i < methods.length; i++) c = new MethodInfo(methods[i]); if (c.getmethodname().equals(setname)) String[] parameters = c.getparameters(); if (parameters[0].equals(java.lang.string)) methodexist = true; assert(methodexist); public void testmodifiers() Person p = new Person(Test); Class cl = p.getclass(); Method[] methods = cl.getmethods(); MethodInfo c; boolean methodexist = false; for (int i = 0; i < methods.length; i++) c = new MethodInfo(methods[i]); if (c.getmethodname().equals(setname)) String modifiers = c.getmodifiers(); if (modifiers.equals(public)) methodexist = true; assert(methodexist); public void testreturntype() 82
83 Person p = new Person(Test); Class cl = p.getclass(); Method[] methods = cl.getmethods(); MethodInfo c; boolean methodexist = false; for (int i = 0; i < methods.length; i++) c = new MethodInfo(methods[i]); if (c.getmethodname().equals(getname)) String returntype = c.getreturntype(); if (returntype.equals(java.lang.string)) methodexist = true; assert(methodexist); public void testtostring() Person p = new Person(Test); Class cl = p.getclass(); Method[] methods = cl.getmethods(); MethodInfo info = null; for (int i = 0; i < methods.length; i++) info = new MethodInfo(methods[i]); if (info.getmethodname().equals(getname)) assertequals(info.tostring(),public java.lang.string getname()); if (info.getmethodname().equals(setname)) assertequals(info.tostring(),public void setname(java.lang.string)); public static Test suite() TestSuite suite = new TestSuite(MethodInfoTest.class); return suite; public static void main(string[] args) String[] testcasename = MethodInfoTest.class.getName() ; junit.textui.testrunner.main(testcasename); 83
84 package test.browser; import junit.framework.test; import junit.framework.testcase; import junit.framework.testsuite; import app.browser.objectinfo; import app.browser.staticfieldinfo; import java.lang.reflect.field; public class StaticFieldInfoTest extends TestCase public StaticFieldInfoTest(String name) super(name); public void testfieldtype() Person p = new Person(TestPerson); Class c = p.getclass(); Field[] personfields = c.getfields(); Field personfield = personfields[0]; StaticFieldInfo f = new StaticFieldInfo(personField); assertequals(f.getfieldtype(), java.lang.string); public void testfieldname() Person p = new Person(TestPerson); Class c = p.getclass(); Field[] personfields = c.getfields(); Field personfield = personfields[0]; StaticFieldInfo f = new StaticFieldInfo(personField); assertequals(f.getfieldname(), _name); public void testmodifiers() Person p = new Person(TestPerson); Class c = p.getclass(); Field[] personfields = c.getfields(); Field personfield = personfields[0]; StaticFieldInfo f = new StaticFieldInfo(personField); assertequals(f.getmodifiers(), public); 84
85 public void testtostring() Person p = new Person(TestPerson); Class c = p.getclass(); Field[] personfields = c.getfields(); Field personfield = personfields[0]; StaticFieldInfo f = new StaticFieldInfo(personField); assertequals(f.tostring(), public java.lang.string _name); public static Test suite() TestSuite suite = new TestSuite(StaticFieldInfoTest.class); return suite; public static void main(string[] args) String[] testcasename = StaticFieldInfoTest.class.getName() ; junit.textui.testrunner.main(testcasename); package test.browser; public class Student public String _name; public int _age; public static int _grade = 55; public Person _teacher; public Student(String name) _name = name; _age = 14; _teacher = new Person(teacher of + name); public void setname(string newname) _name = newname; public String getname() 85
86 return _name; package test.browser; public class Person public String _name; public int _age; public static int _what = 55; public Person(String name) _name = name; _age = 14; public void setname(string newname) _name = newname; public String getname() return _name; public String tostring() return _name; 86
Hvad er Objekter - Programmering
Denne guide er oprindeligt udgivet på Eksperten.dk Hvad er Objekter - Programmering En rigtig god gennemgang af hvad objekter er! Hvordan de oprettes og anvendes! Det er helt klart til nybegyndere, som
METODER ARV KLASSER. Grundlæggende programmering Lektion 5
METODER KLASSER ARV Grundlæggende programmering Lektion 5 1 METODER Sekvenser af kode om samme emne 2 REPETITION Række af statements der udfører en handling Mindst én metode der hedder main Forskellen
Ugeseddel 4 1. marts - 8. marts
Ugeseddel 4 1. marts - 8. marts Læs følgende sider i kapitel 6 i lærebogen: s. 233 258 og s. 291 317 (afsnit 6.3 overspringes). Begynd at overveje, hvad afleveringsopgaven skal omhandle. Læs vejledningen,
AAU, Programmering i Java Intern skriftlig prøve 18. maj 2007
AAU, Programmering i Java Intern skriftlig prøve 18. maj 2007 Opgavebesvarelsen skal afleveres som enten en printerudskrift eller som et passende dokument sendt via email til [email protected]. Besvarelsen skal
Hvad er et distribueret objekt? Plan 12.3. Objekter, objektreferencer, metoder, parameteroverførsel. Objekter: notation
Plan 12.3. Oversigt over grundlæggende begreber Java: eksempel på applikation, programmering og oversættelse Uddybning af grundlæggende begreber Java RMI implementation Forklaring af øvelsen Hvad er et
Test af It-komponent
Test af It-komponent I programmeringssproget Java Programmet Login service Elev: Mads Funch Klasse 2.4 Mat, It, Programmering Skole: Roskilde Tekniske Gymnasium HTX Underviser: Karl Dato: 31-08-2016 Side
Abstrakte datatyper C#-version
Note til Programmeringsteknologi Akademiuddannelsen i Informationsteknologi Abstrakte datatyper C#-version Finn Nordbjerg 1/9 Abstrakte Datatyper Denne note introducerer kort begrebet abstrakt datatype
Polymorfi. Arv (inheritance) Abstrakte klasser, substitutionsprincippet, overriding, statisk og dynamisk type. Coercion
Polymorfi Arv (inheritance) Abstrakte klasser, substitutionsprincippet, overriding, statisk og dynamisk type Coercion Tvangskonvertering (forfremmelse og begrænsning) Oversigt Abstrakt klasse abstrakt
University of Southern Denmark Syddansk Universitet. DM503 Forelæsning 11
DM503 Forelæsning 11 Generics Pakker Exceptions Indhold Generics Nedarvning og Generics Generics Nedarvning og Generics Husk Box fra sidst Generics public class Box {! private T object;! public void
Software Construction 1 semester (SWC) Spørgsmål 1
Spørgsmål 1 Objekter #1 Giv en kort præsentation af begrebet objekt, samt hvorledes du erklærer(declare), opretter(create) og bruger objekter Du kan beskrive o Datatyper o Variable / Instans variable /
Jacob Nordfalk. Ingeniørhøjskolen i København. Nykøbing F itvisioncenter 24. februar 2004
Genbrugelige komponenter og designmønstre i Java Jacob Nordfalk Ingeniørhøjskolen i København Nykøbing F itvisioncenter 24. februar 2004 Program Om Jacob Nordfalk introduktion (ikke-teknisk del) Komponentbaseret
Løsning af møntproblemet
Løsning af møntproblemet Keld Helsgaun RUC, oktober 1999 Antag at tilstandene i problemet (stillingerne) er repræsenteret ved objekter af klassen State. Vi kan da finde en kortest mulig løsning af problemet
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2018 Projekt, del II Institut for matematik og datalogi Syddansk Universitet 13. marts, 2018 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
Tree klassen fra sidste forelæsning
Programmering 1999 Forelæsning 12, fredag 8. oktober 1999 Oversigt Abstrakte klasser. Grænseflader. Programmering 1999 KVL Side 12-1 Tree klassen fra sidste forelæsning class Tree { int age; // in years
Singleton pattern i Java
Denne guide er oprindeligt udgivet på Eksperten.dk Singleton pattern i Java Denne artikel beskriver Singleton pattern og implementation i Java. Den forudsætter kendskab til Java men ikke til Singleton.
DANMARKS TEKNISKE UNIVERSITET
DANMARKS TEKNISKE UNIVERSITET Skriftlig prøve, 14. december 2018, 4 timer Side 1 af 18 Kursus navn: 02101 Indledende Programmering Kursus : 02101 Tilladte hjælpemidler: Ikke-digitale skriftlige hjælpemidler
University of Southern Denmark Syddansk Universitet. DM502 Forelæsning 2
DM502 Forelæsning 2 Repetition Kompilere og køre Java program javac HelloWorld.java java HeloWorld.java Debugge Java program javac -g HelloWorld.java jswat Det basale Java program public class HelloWorld
Kursus i OOP og Java. Kursus i Objektorienteret programmering i Java
Kursus i OOP og Java Kursus i Objektorienteret programmering i Java Åben Dokumentlicens Dette foredragsmateriale er under Åben Dokumentlicens (ÅDL) Du har derfor lov til frit at kopiere dette værk Bruger
RMI introduktion. Denne artikel beskriver Java RMI (Remtote Method Invocation).
Denne guide er oprindeligt udgivet på Eksperten.dk RMI introduktion Denne artikel beskriver Java RMI (Remtote Method Invocation). Den beskriver teorien bag RMI, viser et simpelt kode eksempel og forklarer
Grundlæggende Programmering ITU, Efterår 1999. Skriftlig eksamen i Grundlæggende Programmering
Skriftlig eksamen i Grundlæggende Programmering ITU, 20. januar 2000 Alle hjælpemidler tilladt, dog ikke datamat. Eksamen er skriftlig, fire timer, og bedømmes efter 13-skalaen. Opgavesættet består af
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2018 Projekt, del II Institut for matematik og datalogi Syddansk Universitet 20. marts, 2019 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
Administration af subsites BRUGERVEJLEDNING FOR ADMINISTRATOREN
Administration af subsites BRUGERVEJLEDNING FOR ADMINISTRATOREN Indholdsfortegnelse Introduktion... 2 Definitioner... 2 Generelt... 3 Oprettelse af en skabelon... 4 Sidetypeskabeloner... 5 Globale displaymoduler...
4 Basal Objekt-orienteret Programmering I.
4 Basal Objekt-orienteret Programmering I. Klasser i forhold til abstrakte datatyper og record-typer. Variable og operationer. Klasse-interfaces. Klasser og typer. Klasse-instantiering og initialisering.
Objektorienteret Programmering
Objektorienteret Programmering Struktureret Systemudvikling Jan Bendtsen Automation and Control Indhold Lidt om programmeringssprog Klasser i Java Klasser i C++ Oversættelse og kørsel af kode Et eksempel:
Videregående programmering i Java
Videregående programmering i Java Dag 6 Komponenter (og lidt Swing og MVC) Læsning: VP 4, evt. VP 6 Dette materiale er under Åben Dokumentlicens, se http://www.sslug.dk/linuxbog/licens.html Grafiske komponenter
Objektorienteret design med arv og polymorfi:
Note til Programmeringsteknologi Akademiuddannelsen i Informationsteknologi Objektorienteret design med arv og polymorfi: Substitutionsprincippet Composite Design Pattern Finn Nordbjerg Side 1 Objektorienteret
Videregående Programmering for Diplom-E Noter
Videregående Programmering for Diplom-E Noter 1. Uddelegering Ét af de væsentlige principper i objektorienteret programmering er, at enhver klasse selv skal kunne "klare ærterne". Enhver klasse skal altså
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2019 Projekt, del III Institut for matematik og datalogi Syddansk Universitet 10. april, 2019 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
Videregående Programmering Obligatorisk opgave - 3. semester, efterår 2004
Overvågningssystem Beskrivelse Bagagesorteringssystemet består af et antal skranker (check-in) til modtagelse og registrering af bagage, et automatiseret sorteringsanlæg samt et antal terminaler (gates),
Skriftlig eksamen i Datalogi
Roskilde Universitetscenter Skriftlig eksamen i Datalogi Modul 1 Sommer 1999 Opgavesættet består af 5 opgaver, der ved bedømmelsen tillægges følgende vægte: Opgave 1 15% Opgave 2 15% Opgave 3 8% Opgave
Threads i Java. Denne artikel giver en introduktion til threads i Java. Den beskriver hvad tråde er og forklarer hvordan de bruges i Java
Denne guide er oprindeligt udgivet på Eksperten.dk Threads i Java Denne artikel giver en introduktion til threads i Java. Den beskriver hvad tråde er og forklarer hvordan de bruges i Java Den forudsætter
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2016 Projekt, del I Institut for matematik og datalogi Syddansk Universitet 29. februar, 2016 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
Objektorienterede metoder
Objektorienterede metoder Denne gang: Designmønstre Skabende designmønstre: Fabrikeringsmetode/Fabrik, Singleton, Abstrakt fabrik (Toolkit), Prototype, Objektpulje Singleton eksempel: Forskellige slags
IDAP manual Emission
IDAP manual Emission Dato: 08-06-2005 16:32:35 Indhold INDHOLD... 1 1 EMISSION... 2 1.1 KURVER... 2 1.2 RAPPORTER... 5 1.3 DATA REDIGERING... 6 1.3.1 Masse redigering... 7 1.3.2 Enkelt redigering... 10
University of Southern Denmark Syddansk Universitet. DM502 Forelæsning 3
DM502 Forelæsning 3 Indlæsning fra tastatur Udskrift til skærm Repetition Beregning af middelværdi Gentagelse med stop-betingelse (while) Heltalsdivision Division med nul Type-casting ( (double) ) Betinget
Objects First with Java A Practical Introduction Using BlueJ
Objects First with Java A Practical Introduction Using BlueJ En introduktion til objektorienteret programmering for begyndere ud fra et software engineering aspekt Om at programmere i Java, ikke om værktøjet
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2016 Projekt, del III Institut for matematik og datalogi Syddansk Universitet 20. april, 2016 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
Kursusgang 12. Oversigt: Sidste kursusgang Layout-manager Event-håndtering. Design af brugerflader 12.1
Kursusgang 12 Oversigt: Sidste kursusgang Layout-manager Event-håndtering Design af brugerflader 12.1 Sidste kursusgang Oversigt: Sidste kursusgang Værktøjer til udvikling og implementering af HCI-design
DM502. Peter Schneider-Kamp ([email protected]) http://imada.sdu.dk/~petersk/dm502/
DM502 Peter Schneider-Kamp ([email protected]) http://imada.sdu.dk/~petersk/dm502/ 1 DM502 Bog, ugesedler og noter De første øvelser Let for nogen, svært for andre Kom til øvelserne! Lav opgaverne!
SWC eksamens-spørgsmål. Oversigt
SWC eksamens-spørgsmål Oversigt #1 Typer og variable #2 Aritmetik og logik #3 Klasser (definition, objekter) #4 Klasser (metoder) #5 Klasser (nedarvning, polymorfi) #6 Conditional statements #7 Repetition
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2019 Projekt, del I Institut for matematik og datalogi Syddansk Universitet 27. februar, 2019 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
3 Algebraisk Specifikation af Abstrakte Datatyper.
3 Algebraisk Specifikation af Abstrakte Datatyper. Specifikation kontra program. Bestanddele af en algebraisk specifikation. Klassificering af funktioner i en ADT. Systematisk definition af ligninger.
Klasser og Objekter i Python. Uge 46 Learning Python: kap 15-16, 19-22.
Klasser og Objekter i Python Uge 46 Learning Python: kap 15-16, 19-22. Klasser og objekter En klasse beskriver en klump af samhørende funktioner og variable En klasse er en beskrivelse. En kage form Klassens
Anvendelse af metoder - Programmering
Denne guide er oprindeligt udgivet på Eksperten.dk Anvendelse af metoder - Programmering En forhåbentlig rigtig god forklaring på hvad metoder er og hvordan de anvendes. Lidt om private og public, retur
Indledning. Hvorfor det forholder sig sådan har jeg en masse idéer om, men det bliver for meget at komme ind på her. God fornøjelse med læsningen.
Indledning...2 Variabler...13 Eksempel: 1...13 Eksempel 2:...13 Eksempel 3:...15 Eksempel 4:...16 Metoder...17 Metode (intet ind og intet ud)...17 Metode (tekst ind)...18 Metode (tekst ind og tekst ud)...19
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2017 Projekt, del III Institut for matematik og datalogi Syddansk Universitet 6. april, 2017 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
Andreas Lauge V. Hansen klasse 3.3t Roskilde HTX
IT -Eksamen Andreas Lauge V. Hansen klasse 3.3t Roskilde HTX [Vælg en dato] Indhold Indledning... 2 Teori... 3 Hvorfor dette design... 4 Produktet... 4 Test og afprøvning... 9 Konklusion... 10 Indledning
Rename og redefine. Abstrakte klasser. Dynamisk binding.
11 Nedarvning II. Enkeltnedarvning i Eiffel. Rename og redefine. Initialisering af superklasse-dele af et objekt. Interfaces til klienter og subklasser. Typesammenlignelighed og polymorfi. Abstrakte klasser.
Tabelbegrebet. Klassediagrammer (III) Oversigt. Anvendelse af Tabeller. Tabeller og qualified associations
Tabelbegrebet Klassediagrammer (III) Tabeller og qualified associations originally by Michael R. Hansen modified/extended by Anne E. Haxthausen Informatics and Mathematical Modelling Technical University
Opret prøve og tilpas dit fronter-rum Spørgsmålstyper og justering Oversigt over spørgsmålstyper...20 Justering af spørgsmål og sider...
PRØVEVÆRKTØJ Table of Contents Opret prøve og tilpas dit fronter-rum... 3 Opret prøve... 4 Tilføj prøveværktøj... 6 Fanen "Indstillinger"...11 Indstillinger for vindue...15 Mappe til billeder/multimedier...17
Introduktion til ActionScript
Introduktion til ActionScript Kaspar Rosengreen Nielsen [email protected] i n t e r a c t i v e s p a c e s. n e t Kaspar Nielsen, [email protected] 1 Dagens program Opsamling på
Udvidelse og specialisering. Klassehierarkier. Nedarvningsterminologi. Interfaces. Statiske og dynamiske typer. Polymorfi. Abstrakte klasser.
10 Nedarvning I. Udvidelse og specialisering. Klassehierarkier. Nedarvningsterminologi. Interfaces. Statiske og dynamiske typer. Polymorfi. Dynamisk binding og virtuelle operationer. Decentraliseret/centraliseret
Bemærk! Et PHP script har kun brug for at forbinde én gang til databaseserveren. Det kan så sagtens udføre flere kommandoer vha. denne forbindelse.
Mysqli Webintegrator Når vi arbejder med server-side scripting ( i vort tilfælde PHP), har vi ofte behov for at kunne tilgå data, som vi opbevarer i en database. Det kan f.eks. dreje sig om nyhederne i
UML til kravspecificering
UML til kravspecificering UML mini-kompendium - til brug i forbindelse med modellering af kravspecifikationer. Copyright 2006 Teknologisk Institut, IT-Udvikling Aktivitetsdiagram 2/9 Aktion Aktionsnavn
