Arv och klassbibliotek Johan L Larsson Uppsala Universitet/KTH November 1999 [email protected] 1 Klasser och objekt Car p a rtic ula rC a r in sta n tia te s [email protected] 2 Introduktion till arv • Problem med modifikation av slutna moduler: man vill helst undvika att göra ändringar. • Det är naturligt att organisera många problem hierarkiskt, dvs i olika nivåer. • Arv är “delta-change”, dvs ändring genom att ange skillnad mellan klasser. • En subklass ärver från en superklass och får då allt som superklassen har, plus mer! [email protected] Exempel Vehicle Land Vehicle Car Truck 3 Water Vehicle Boat Submarine Air Vehicle Airplane Rocket Antag att Vehicle har: • Attributet fSpeed • Metoden accelerate() Då har alla klasserna åtminstone: • Attributet fSpeed • Samma metod accelerate() … men dom kan ha mer! [email protected] 4 Olika typer av arv • Enkelt arv (“single inheritance”) – Klasserna bildar hierarkier där varje klass har endast en superklass. • Multipelt arv (“multiple inheritance”) – Klasserna bildar komplexa grafer där en klass kan ärva från flera andra klasser. – Problem med entydighet, komplicerade beroenden, etc, gör att Java saknar detta. • Interface-”arv” (som i Java) – Ren subtypning (utan att kod ärvs) [email protected] 5 Typer - 1 • En typ är en mängd värden och en mängd operatoner på dessa värden. • Typer låter oss på ett säkert sätt para ihop operationer med de värden där de tillåts. • Typfel kan antingen hanteras av kompilatorn O bject eller under körningen. • = statisk (“strong”) respektive (“weak”) dynamisk typning Message Type error (message not found) [email protected] 6 Typer - 2 • Typer ger oss säkrare programmering. • Statisk eller dynamisk typning? Industrin föredrar traditionellt statisk typning som i Ada, Pascal, C, C++, Java, Eiffel m fl: + Undviker körningsfel redan vid kompilering + Gör program mer tillförlitliga i potentiellt farliga situationer, t ex i ett flygplan. + Snabbare körning (typkontroll är kostsamt) − Mindre flexibel programvara (jfr Smalltalk) [email protected] 7 Konstruktion av (sub-) typer • Viktig egenskap hos OO PL: användare kan bygga nya typer från gamla: Genom att “köpa”, dvs låta klasser innehålla attribut av andra klasser precis som strukturer i Pascal eller C (record/struct). Genom arv. Nya subtyper V ehicle ärver från gamla typer, a is eventuellt med tillägg C ar h a s-a W heel (attribut, metoder). [email protected] 8 Dynamisk bindning • Bidning: ett väre måste ges en typ innan den kan användas. • På samma sätt måste objekt ges en klass innan de mottar meddelanden, etc. • Dynamisk bindning = ett objekt ges en klass vid tilldelning, ej vid deklaration. [email protected] 9 ”Principle of Substitutability” • Instanser av en subklass innehåller alla attribut som finns i en superklass. • Instanser måste ”kopiera” superklassens alla metoder. • Därför kan en subklass härma en superklass så att vi kan behandla dem lika. If we have two classes A and B, such that class B is a subclass of A, it should be possible to substitute instances of clss B for instances of class A in any situation with no observable effect. [Budd96] [email protected] 10 Specialisering med arv • En subklass kan vara en specialisering av superklasen. T ex är ”bil” en specialisering av ”fordon”. • ”Window” kan specialiseras till ”TextWindow” • Raffinering (”refinement”) av superklassen • Subklassen blir också en subtyp • Subklassen kan bytas ut mot superklassen närsomhelst. [email protected] 11 Arv som specifikation • Arv kan garantera att Mer Mer en subklass uppfyller generell specifik en specifikation (gränssnitt, etc). • Till skillnad från ”specialisering” är • Abstrakta klasser är superklassen inte klasser som inte kan komplett utan måste användas - dom fyllas med funktion, saknar metoder etc helt eller devis. [email protected] 12 Arv som konstruktion (är-del-av) • En klass kan ärva från en eller flera klassar för att få all sin funktionalitet. • Man plockar då in beteende från klasser av intresse och använder arv för detta. • Bryter ofta ”principle of substitutability”: subklasser blir inte alltid subtyper. • Privat arv löser problem med arv för konstruktion. [email protected] 13 Arv som generalisering • Motsats till specialisering • Exempel: ColoredWindow ärver från Window och blir ett mer generaliserat fönster. Det har ju färg. • Bygger ofta på tillägg av attribut, t ex färg i exemplet. • Bör undvikas: vänd istället typhierarkin. [email protected] 14 Arv för utökning • Utökning (”extension”) = expandera med funktionalitet i superklassen som är helt nytt (dvs ej raffinering av gamla funktioner) • StringSet som kan tänkas ärva Set tillhandahåller säkert flera helt nya metoder. De är utökningar av superklassen. • Finns någon fördel med att lämna gammal funktionalitet ”som den är”? [email protected] 15 Arv för kombination • Man vill ibland kombinera funktionalitet ifrån flera klasser i en enda klass (multipelarv) • Detta kan ge problem om funktionalitet kolliderar (t ex metoder har samma namn) [email protected] 16 Arv för begränsning • Om en klass ärver från en superklass med funktionalitet som ej behövs kan subklassen begränsas • Man kan då överskriva med tomma metoder och eventuellt göra anrop till programfel. • Bör aldrig användas! (Varför?) [email protected] 17 En liten slutsats • Arv kan användas på ett dåligt sätt om vi inte tänker oss för! [email protected] 18 Överskrivning • Att ersätta en metod med en ny metod genom arv kallas överskrivning • En form av polymorfism som baserar sig på dynamisk bindning • Vi använder virtual (Java, C++, ObjectPascal) för att markera vilka metoder som inte binds fast då vi deklarerar klasser • Virtuella metoder binder meddelanden till objekts klasser under körningen [email protected] 19 Kovarians • Överskrivning av en metod med en parameter som blir en subklass, dvs parametern ”följer efter arvsriktningen”. se tD rive r (D rive r d ); Driver Vehicle Mer generell TruckDriver Car Truck se tD rive r (T ru ckD rive r d ); • Kan ge problem med typkompatibilitet (Truck.setDriver accepterar bara en delmängd av typerna) [email protected] 20 Kontravarians • Överskrivning av en metod där parametern blir en superklass. setDriver (TruckDriver d); Driver Vehicle Mer specifik TruckDriver Car Truck setDriver (Truck d); [email protected] 21 Avancerad överskrivning • Vi kan ställa krav på överskrivningen: – En viss grupp av metoder måste överskrivas samtidigt. – Inramade metoder: tillåt överskrivning med ”before method” eller ”after method” (CLOS) – Kovarians och kontravarians. [email protected] 22 Multipelarv - 1 • ”Diamond-inheritance” + om man tänker sig arv som kombination av beteenden i superklasser blir multipelarv en naturlig utökning. Ex. C++ och Eiffel. + Modellering blir nu annorlunda - vad som var svart med enkelt arv blir nu lätt. N um ber M agnitude Integer [email protected] 23 Multipelarv - 2 – Tvetydighet med namn: • Byt namn utan att ändra metod • Definiera om metoder/attribut – Delad superklass: • Dubbla kopior av attribut etc. • Virtuella superklasser som i C++ [email protected] S tream In S tream O u tStre am In O u tStre am 24 Några fler problem med arv • Hur kan vi ändra klasser mitt i hierarkier, eller är vi begränsade till utkanten av hierarkierna? • Är det lätt att läsa långa klasshierarkier där ett visst objekts beteende kan härstamma från väldigt många klasser? Jojo-effekt. • Kan vi använda en enskild klass i ett annat program eller måste vi flytta över en hel hierarki av klasser? • Blir verkligen subklass en subtyp? Kontrakt? [email protected] 25 Alternativ till arv • Enkelt arv (”single inheritance”) – Komplettera med nästade klasser – Komplettera med interface-arv • Multipelt arv – Ger stora möjligheter men ibland problem • Inget (implementations-) arv: – Subtyper (interface-arv) – Has-a relationer (klasser som attribut) [email protected] 26 Klassbibliotek och hierarkier • Språk såsom Java och Eiffel tillhandahåller ett stort antal klasser i ett så kallat klassbibliotek. • Klassbibliotek baserar sig på arvsrelationer. • Klassbibliotek ska inte ändras. Vi använder arv för att överskriva beteende. • Klassbibliotek kan använda abstrakta klasser som vi måste utöka beroende på vad vi exakt vill använda biblioteket till. [email protected] 27 Klassbiblioteket i Eiffel Array Array Fixed Fixed List List List List Node Node List List Linked Linked List List Bi-List Bi-List Node Node Bi-Linked Bi-Linked List List Tree Tree Node Node [email protected] 28 Att lära av andra = ”reuse” • Återanvändning! • Genom att använda arv kan vi använda klassbibliotek utan att göra modifikationer. • Vi slipper förstå detaljer om klasser och behöver bara första ett fåtal metoder även om en mängd metoder används. [email protected] 29 Att bygga klassbibliotek • Det är svårt att bygga bra klassbibliotek! – Klasserna måste vara tillräckligt öppna för utökningar och anpassningar – Klasserna får inte vara inlåsta i arv (vi behöver has-a relationer för att det ska bli flexibelt) – Vi måste välja rätt arvsrelationer: t ex vad ska egentligen finnas i den gemensamma superklassen för alla datastrukturer – Vi måste förstå hur biblioteket ska användas! [email protected] 30 Ett steg mot frameworks • Objekt-orienteade frameworks är ett aktuellt forskningsområde! • Ett framework är ett bibliotek av klasser som vävts mycket tätt samman. De bildar en ”abstrakt applikation” med extremt mycket gömda detaljer. • Frameworks är svåra att konstruera, svåra att lära sig - men mycket kraftfulla för återanvändning! [email protected] 31 Nästade klasser • Klasser kan vara medlemmar i andra klasser i t ex Java och C++: class EnclosingClass { ... class NestedClass { … } } • Nästade klasser löser problem med encapsulation: den nästade klassen kommer åt den yttre klassens medlemmar, även de som är deklarerade som private. • Underlättar organisering av kod. [email protected] 32 Motivation till nästade klasser // An example with cluttered but valid code public class MyClass implements MouseListener { ... someObject.addMouseListener(this); ... /* Empty method definitions. */ public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseClicked(MouseEvent e) { } } • Svårt att utöka och mycket kod att skriva. [email protected] 33 Nästade klasser i Java • Vi vill ha en inre klass MouseAdapter som vi senare kan utöka. // An example of using an inner class public class MyClass extends Applet { ... someObject.addMouseListener(new MyAdapter()); ... class MyAdapter extends MouseAdapter { public void mouseClicked(MouseEvent e) { … } } } • Annat exempel: iterator som inre klass till datastrukturer [email protected] 34 Polymorfism: en överblick P olym orphism A d hoc C oercion U niversal O verloading Inclusion S ub-type Code P aram etric O b je cts [email protected] 35 Några uppgifter • Uppgift 1: ge konkreta exempel på när det är bättre med arv än komposition (has-a)! • Uppgift 2: ge konkreta exempel på när det är bättre med komposition (has-a) än arv! • Uppgift 3: ge motivering till varför en subklass inte behöver vara en subtyp (med exempel)! [email protected] 36