Material för betyg 4-5 Föreläsning nr 5, fredag 27 mars. kl. 13-15. Snabböversikt över: Ärvning, inheritance, bokens kapitel 8-9 (plus bl.a. Filter i projektet imageviewer, kapitel 11) Hantering av "undantag", exceptions. bokens kapitel 12. Frågor ?
Ärvning, inheritance. Projektet imageviewer i kapitel 11: Klassen Filter är märkt abstract och om vi går in med editorn och tittar i koden för dess klassen hittar vi abstract igen: public abstract void apply(OFImage image); Reserverade ordet abstract betyder att klassen Filter inte själv kan skapa objekt. De heldragna pilarna från DarkerFilter etc. till Filter anger att dessa är subklasser till Filter.
Ärvning, inheritance. Projektet imageviewer i kapitel 11: Hur samverkar då subklasserna DarkerFilter, LighterFilter, FishEyeFilter och ThresholdFilter med sin superklass Filter? I klassen ImageViewer anropas konstruktorn i DarkerFilter enligt: new DarkerFilter("Darker"), där aktuella parametervärdet "Darker", senare dyker upp som ett menyalternativ i Filter-menyn. Konstruktorn i DarkerFilter: public DarkerFilter(String name) { // super ( parameter ) betyder just anrop till super(name); // superklassens konstruktor med angiven } // parameter tar bara emot inparametern "Darker", och skickar den vidare som parameter till konstruktorn i superklassen: public abstract class Filter { private String name; // aktuella parametern name vid anropet till public Filter(String name) // konstruktorn i subklassen blir ett värde på { // superklassens strängobjektet name i det this.name = name; // objekt som sub- och superklasserna hjälps } // åt att skapa. Notera att namnet name här (olyckligtvis) används i två betydelser: name lokalt inom konstruktorn och name i klassen Filter för övrigt. Reserverade ordet this i konstruktorkoden kan tolkas som "det objekt som nu skapas".
Ärvning, inheritance. Projektet imageviewer i kapitel 11: Hur samverkar då subklasserna DarkerFilter, LighterFilter, FishEyeFilter och ThresholdFilter med sin superklass Filter? (forts) Anropet till konstruktorn i DarkerFilter leder till att det skapas ett DarkerFilter-objekt, som dels kan utföra metoderna i klassen DarkerFilter, (där finns förutom konstruktorn bara metoden: public void apply(OFImage image) ), dels metoderna i superklassen Filter , (där finns förutom konstruktorn bara metoden: public String getName( ) ) Detta är dock precis det som behövs för att ImageViewer skall kunna använda DarkerFilter (och de andra subklasserna på samma sätt). När man lägger till en ny filtertyp, dvs en ny subklass t ex MirrorFilter, behöver man i ImageViewer bara lägga till en enda rad, ett anrop till den nya subklassens konstruktor och lägga in det returnerade objektet i ett List-objekt filterList: filterList.add ( new MirrorFilter (”Mirror") );
Ärvning, inheritance. Projektet imageviewer i kapitel 11: Koden som i superklassen Filter som lägger in nya filtertypen i Filter-menyn är (filters är samma objekt som filterList i förra bilden): for (final Filter filter : filters) { item = new JMenuItem ( filter.getName( ) ); item.addActionListener ( new ActionListener( ) { public void actionPerformed ( ActionEvent e ) { applyFilter ( filter ); } }); menu.add(item); Texten som dyker upp i menyalternativet kommer från: filter.getName( ) dvs ”Mirror” i exemplet i förra bilden, det som händer vid detta val är att metodanropet applyFilter(filter) utförs, och filter är då en refererens till det MirrorFilter-objekt som skapades och lades in i listan, och i applyFilter anropas just detta MirrorFilter-objekts metod apply(currentImage) där currentImage är en referens till det OFImage-objekt, som skall filtreras.
Ärvningsexemplet ImageViewer, kommentarer: Bokens exempel är ofta konstruerade för att tydligt belysa olika saker, i det här fallet vad det reserverade ordet abstract innebär, och hur abstrakta klasser används. Här behöver varje filterobjekt ha en egen metod public void apply(OFImage image) för att kunna utföra just sin filtrering. Superklassen Filter har ingen egen "defaultmetod" utan apply-metoden finns endast i de olika subklasserna, där den måste finnas. Detta anges i Filter genom nyckelordet abstract för både klassen och metoden apply. Det måste finnas subklasser som kompletterar, extends Filter, med denna metod apply. Man kan ha ärvning, extends, utan att superklassen är abstract. Detta hade varit fallet om Filter haft en egen "defaultmetod" apply. Man kan dock inte ha abstrakta klasser utan att ha ärvning. Eftersom enda skillnaden mellan de olika filterklasserna är, att de har olika sätt att utföra metoden apply, kunde man (nästan?) lika gärna ha lagt in alla dessa som skilda metoder i klassen Filter. Uppdateringen av klassen ImageViewer med nya menyalternativ, när man lägger till en ny filertertyp gjordes ju ändå för sig och manuellt. En alternativ implementation, utan ärvning och med automatisk uppdatering av menyalternativen, finns i projektet AltImageViewer, men där får man ju inte tillfälle att diskutera abstrakta klasser och metoder alls.
Flera ärvningsexempel: Från standardbibliotek En mycket vanlig och användbar tillämpning av ärvning är, att man vill att en klass, förutom sin egen funktionalitet, skall kunna utföra en mängd funktioner/tjänster som finns i javas bibliotek. I t.ex. en interaktiv tillämpning, där användaren skall administrera sitt adressregister, vill man kunna lägga till nya kontakter, ändra uppgifter om eller helt ta bort befintliga, göra sökningar och listningar mm. Alt detta vill man göra som en interaktiv fönsterapplikation. Ett bekvämt sätt att börja är då: import java.awt.event.*; import javax.swing.*; import java.awt.*; import java.util.*; public class AdressRegister extends JFrame { // osv Ett AdressRegister-objekt kan då automatisk utföra allt det som ett JFrame-objekt kan, och dessutom allt det man lagt in i klassen AdressRegister.
public void makeVisible ( ) ; Flera ärvningsexempel: Begreppshierarkier Det allra första exemplet i kursboken handlade om cirklar, trianglar och rektanglar. I klasserna Circle, Square och Triangle fanns väldigt mycket som var gemensamt, nämligen variablerna: int xPosition; int yPosition; String color; boolean isVisible; och metoderna: public void makeVisible ( ) ; public void makeInvisible ( ) ; public void moveRight ( ) ; public void moveLeft ( ) ; public void moveUp ( ) ; public void moveDown ( ) ; public void moveHorizontal(int distance) public void moveVertical(int distance) public void slowMoveHorizontal(int distance) public void slowMoveVertical(int distance) public void changeColor(String newColor) private void erase ( ) ;
Flera ärvningsexempel: Begreppshierarkier forts Det som skiljer när det gäller variabler (fields) är att cirkelobjekt har diameter, kvadrater size (=sida) och trianglar width och height, och när det gäller metoder att rita dem med: public void draw( ); // olika biblioteksfunktioner används och anger storlek: public void changeSize(int newDiameter); // circle, one parameter public void changeSize(int newSize); // square, one parameter public void changeSize(int newHeight, int newWidth); // triangle, two parameters Genom att lägga det gemensamma i en superklass Shape kan man minska den totala kodmängden och framför allt göra den lättare att underhålla. Lite extra trixande krävs för att hantera det olika antalet parametrar till changeSize. Se projektet shapesPlus.
Flera ärvningsexempel: Begreppshierarkier forts 2 Ett projekt i tre versioner, dome-v1, dome-v2 och dome-v3 i kapitel 8-9, illustrerar hur man i det fallet på liknande sätt kan undvika att samma kod finns på flera ställen. Mitt intryck är nog att ärvningshierarkierna förekommer mer frekvent i kursböcker om objektorienterad program-mering än de gör i praktiska industriella tillämpningar. I anslutning till dome-projekten illustreras också dynamisk bindning och skillnaden mellan static eller declared type, och dynamic eller run-time type.
Om hantering av "undantag", exceptions, i java, kursbokens kap. 12: När man designar ett datorprogram utgår man normalt från en normal och fungerande driftmiljö. Det är i denna programmet främst skall fungera. I nästa steg kompletterar man med att ta hänsyn till ett antal förutsägbara felsituationer: användaren kan mata in felaktiga data, klicka på menyer och knappar i felaktig ordning, nätverksförbindelsen kan försvinna, den sökta filen eller nätverksadressen finns inte, resultatet av en operation kan ge ett resultat som inte kan hanteras, etc. Normalt vill man inte att programmet skall spåra ur eller haka upp sig, när denna typ av händelser inträffar. Skall programmet vara robust och kunna hantera sådana felsituationer eller ”undantag”, exceptions, kan koden för detta ofta bli mycket mer omfattande, än koden för den ”normala” funktionen. Man vill dock gärna behålla en överskådlig struktur för ”normalkoden” och på något sätt separera den från ”felhanteringskoden”. Java har (naturligtvis) en objektorienterad modell för felhanteringen. Metaforen är att en felhändelse genererar ett exception-objekt, som kastas, throw, iväg från den plats i koden, där händelsen inträffar. Exception-objektet ”fångas”, catch, någonstans, och där bestäms vilken åtgärd som är lämplig. Man återvänder sedan inte automatiskt till platsen, där felhändelsen inträffade.
Om hantering av "undantag", exceptions, i java, kursbokens kap Om hantering av "undantag", exceptions, i java, kursbokens kap. 12, forts: Vissa exceptions är det programmerarens skyldighet att vara beredd på; man skall veta att de kan inträffa vid vissa operationer. Andra exceptions är sådana att de kan inträffa nästan var som helst. Ett av de vanligaste av dessa är NullPointerException, som genereras om man ber ett objekt som (ännu) inte finns att utföra en metod. För de exceptions man kan förutse, gäller detta ofta ett visst kodavsnitt. Om jag skriver ett program, som skall läsa in en fil bör jag vara beredd t.ex. på att filen inte finns. Jag lägger då filopera-tionerna i ett try-block, och engagerar en eller flera catch-funktioner, som håller ett vakande öga (readFromFile är pseudokod): String textrad =””; try { while(true) { textrad = readFromFile(”minFil.txt”); System.out.println(textrad); } catch (FileNotFoundException ex1) { System.out.println( ”Felmeddelande: ”+ex1); } catch ( EOFException ex2) { ; // slut på fil: färdigläst – bara att gå vidare} … kodfortsättning efter ovanstående Om det i try-blocket ”kastas” ett FileNotFoundException avbryts direkt try-satserna och det som finns tillhörande catch-block (det första) utförs. Därefter ”kodfortsättningen. Inträffar EOFException avslutas också try-satserna och man går direkt till ”kodfortsättningen, eftersom koden i catch-block 2 är tom. Skulle något annat exception kastas, flyger det vidare och får fångas av omgivande block eller kasta vidare ända ut till java-interpretatorn, som då avbryter programmet.
Om hantering av "undantag", exceptions, i java, kursbokens kap Om hantering av "undantag", exceptions, i java, kursbokens kap. 12, forts 2: Normalt är de exceptions, som redan finns i biblioteken, fullt tillräckliga. Det finns dock möjlighet att definiera egna Exception-klasser, som då är subklasser till superklassen Exception. Ett catch-block: catch (Exception e) { pseudokod: hantering;} fångar alla typer av exceptions, dvs ”kastade” objekt från klassen Exception och alla dess subklasser. Vill man definiera en egen exception-klass är mekanismen för att kasta sådana exception-objekt: if ( filnamn.equals(bittorrent.exe) ) throw new AlertAntiPiratException ( ”misstänkt fildelare”); … kodfortsättning efter ovanstående Om en metod exempel innehåller operationer som explicit kan leda till exceptions, säg IOException som inte ”catchas” måste detta anges med throws som i: public void exempel() throws IOException { … metodens kod } Detta kan sedan ”kastas” vidare från metoder som anropar denna metod etc. Nyckelord i samband med exceptions: throw, throws, try, catch och Exception.
Individuella projektet: Syfte och krav? Frågor och diskussion allmänt. Kursen hittills: Klasser, objekt, konstruktorer (och anrop till), parameterlistor, datatyper (primitiva och klasser), metoder (och anrop till), returvärden, returtyper, "anropskedjor", deklarationer, räckvidd/synlighet/scope, de 53 reserverade orden, Individuella projektet: Syfte och krav? Databasuppgiften: Syfte och krav? Betyg 4-5: Hur och när? (FL-6) Övrigt?