Föreläsningar nov 5 v 45. Objektorientering och arv. Komplexa tal definierade cartesiskt eller polärt. Komplexa tal kan beskrivas på två sätt, t ex kan samma komplexa tal kan skrivas som 3 + 4i eller som 5e i*fi där fi = arctan (4/3); Genom att att använda arv kan man i Objekt-orienterade språk i denna och liknade situtioner skapa en supertyp (som vi i vårt fall döper till Complex) som har två subtyper för de olika sätt som vi kan skapa komplexa tal (vi döper de två varianterna till Cart och Polar). I UML: Om nån metod abstrakt hela klassen abstrarat kan ej instansieras Klassnamnet kursiverat Complex public abstract double re(); public abstract double im(); public abstract double modulus(); public abstract double argp(); public abstract boolean equals(complex z2); public abstract Complex conjugate() public Complex add(complex z2) public Complex sub(complex z2) public Complex mult(complex z2) public Complex div(complex z2) ) betyder ärver, skrivs med extends i Java abstrakta metoder saknar kropp, måste implemnteras någonstans i ärvande klasser. I UML kursivt konkreta metoder har kropp, finns gratis i objekten av ärvande klass. Cart private double real private double imag public Cart(double ire, double iim) public double re() public double im() public double modulus() public double argp() public boolean equals(complex z2) public Complex conjugate() Polar private double rr private double arg public Polar(double ir,double.. public double re() public double im() public double modulus() public double argp() public boolean equals(complex z2 public Complex conjugate()
I Java kan detta implementeras på detta sätt: public abstract class Complex { public abstract double re(); public abstract double im(); public abstract double modulus(); public abstract double argp(); public abstract boolean equals(complex z2); public Complex add(complex z2){ return new Cart(this.re() + z2.re(), this.im() + z2.im()); public Complex sub(complex z2) { return new Cart(this.re() - z2.re(), this.im() - z2.im()); public Complex mult(complex z2) { return new Polar(this.modulus()*z2.modulus(), this.argp() + z2.argp()); public Complex div(complex z2) { return new Polar(this.modulus()/z2.modulus(), this.argp() - z2.argp()); { return re() + " + " + im() + "i" + "\t " + modulus() + "*exp(i*" + argp() + ")"; public class Cart extends Complex { private double real; private double imag; public Cart(double ire, double iim) { real = ire; imag = iim; public boolean equals(complex z2) { return (real == z2.re()) && (imag == z2.im()); public double re() { return real;
public double im(){ return imag; public double modulus(){ return Math.sqrt(real*real + imag*imag); public Complex conjugate() { return new Cart(real, -imag); public double argp(){ if (real > 0.0 ) { return Math.atan(imag/real); else if (real == 0.0 && imag == 0.0){ return 0.0; else if (real == 0.0 && imag > 0.0){ return Math.PI; else if (real == 0.0 && imag < 0.0){ return - Math.PI; else { return Math.atan(imag/real) + Math.PI; public class Polar extends Complex { private double rr; private double arg; public Polar(double ir, double iarg) { rr = ir; arg = iarg; public boolean equals(complex z2) { return (rr == z2.modulus() ) && (arg == z2.argp()); public double re() { return rr*math.cos(arg); public double im(){ return rr*math.sin(arg); public Complex conjugate() { return new Polar(rr, -arg);
public double modulus(){ return rr; public double argp(){ return arg; De konkreta (dvs icke-abstrakta) subtyperna (klasserna) har som förut privata instansvariabler, subtypen Cart har en instansvariabel för det komplexa talets realdel och en instansvariabel för det komplexa talets imaginärdel medan subtypen Polar har en instansvariabel för det komplexa talets modulus (belopp) och en instansvariabel för det komplexa talets "argument" (vinkel). Liksom förut måste vi kunna skapa nya komplexa tal, vilket vi som förut gör med konstruktorerna för de två olika konkreta subtyperna. Vi kan alltså skapa ett komplext tal på två olika sätt. Delar av algebran för komplexa tal har definierats med metoder redan i Complex, och behöver då inte definieras i subtyperna, medan andra definieras i subtyperna. Rubrikerna för de senare har då deklarerats abstrakt i Complex för att tvinga programmeraren att definiera metoderna senare i subtyperna. En abstrakt metod saknar kropp, som ersätts med semikolon. I rubriken skall ingå ordet abstract. En klass med (minst) en abstrakt metod måste deklareras med abstract. Ett testprogram : public class TestComplex { public static void main(string [] iargs) { System.out.println(" (3+4i)= " + new Cart(3, 4)); System.out.println(" (3+4i)/(1-2i) = " + (new Cart(3, 4)).div(new Cart(1, -2))); System.out.println(" (3+2i)(4+5i) = " + (new Cart(3, 2)).mult(new Cart(4, 5))); System.out.println(" Ex 10.2.14 = " + ((new Cart(1, -2)).div(new Cart(3, 4)).sub( (new Cart(2, 1)).div(new Cart(0, 5))))); System.out.println(); Complex z1 = new Cart(3, 4); // Cart subtyp till Complex.OK Complex z2 = new Cart(1, -2); //Finns inga objekt som är "bara" Complex Complex z3 = new Cart(3, 2); // Ok ändå Complex z4 = new Cart(4, 5); Complex z5 = new Cart(2, 1); Complex z6 = new Cart(0, 5); System.out.println(" (3+4i)= " + z1); System.out.println(" (3+4i)/(1-2i) = " + z1.div(z2)); //div OK på subtyper System.out.println(" (3+2i)(4+5i) = " + z3.mult(z4)); System.out.println(" Ex 10.2.14 = " + (z2.div(z1)).sub((z5).div(z6))); System.out.println(); Complex z11 = new Polar(5, 0.9272952180016122);// Polar subtyp till Complex.OK System.out.println(" (3+4i)= " + z11); System.out.println(" (3+4i)/(1-2i) = " + z11.div(z2));
/* Körresultat : java TestComplex (3+4i)= 3.0 + 4.0i 5.0*exp(i*0.9272952180016122) (3+4i)/(1-2i) = -1.0 + 2.0i 2.23606797749979*exp(i*2.0344439357957027) (3+2i)(4+5i) = 1.999999999999999 + 22.999999999999996i 23.086792761230388*exp(i*1.4840579881189115) Ex 10.2.14 = 0.2 + -0.19999999999999993i 0.282842712474619*exp(i*-0.7853981633974481) (3+4i)= 3.0 + 4.0i 5.0*exp(i*0.9272952180016122) (3+4i)/(1-2i) = -1.0 + 2.0i 2.23606797749979*exp(i*2.0344439357957027) (3+2i)(4+5i) = 1.999999999999999 + 22.999999999999996i 23.086792761230388*exp(i*1.4840579881189115) Ex 10.2.14 = 0.2 + -0.19999999999999993i 0.282842712474619*exp(i*-0.7853981633974481) (3+4i)= 3.0000000000000004 + 3.9999999999999996i 5.0*exp(i*0.9272952180016122) (3+4i)/(1-2i) = -1.0 + 2.0i 2.23606797749979*exp(i*2.0344439357957027) Process TestComplex finished */ Om arv. Abstrakta klasser kan inte instansieras, men klassnamnet kan användas som typ och alla subtyper innefattas i denna "supertyp". Metoder och variabler i en klass som är private kan bara användas i klassen själv, inte ens i ärvande klasser. Genom att förklara metoder och variabler protected kan de användas i ärvande klasser, detsamma gäller förståss om de är public men de vill man ofta inte, speciellt för variabler eftersom klassen då ej skulle bli en abstrakt datatyp (ADT). Metoder och variabler som finns i en superklass finns alltså normalt automatiskt i subklasser. Men man kan också omdefiniera (override) metoder och variabler i en superklass i en subklass. Omdefiniering (override) är något annat än överladdning (overload).vill man ändå använda metoder och variabler i en superklassen skriver man super. före namnet på metoden eller variablen i en superklassen. super kan ensamt även användas för att komma åt konstuerare i superklasser. Alla klasser som inte ärver nån annan klass ärver automatiskt klassen Object. Alla klasser ärver följdaktligen klassen Object, direkt eller indirekt. Fylligare och mer om arv i DD kapitel 9, vilket kanske är bokens viktigaste kapitel.
Exempel : DD 9.16 Case Study A PayrollSystem Employee private String firstname; private String lastname; public Employee(String first, String last) public String getfirstname() public String getlastname() ; public abstract double earnings); Boss private double weeklysalary public Boss(String first, String last, doubly salary) public void setweeklysalary(doubly salary) public double earnings() PieceWorker private double wageperpiece private int quantity public PieceWorker(String first, String last, double wage, int numberofitems ) public void setwage(doubly wage) public setquantity(int number of Items) public double earnings() HourlyWorker private double wage private double hours public HourlyWorker(String first, String last, doubly wageperhour, double hoursworked) public void setwage(doubly wageperhour) public void sethours(double hoursworked public double earnings() CommisionWorker private double salary private double commision private int quantity public CommisionWorker(String first, String last, doubly salary, dobly commision, int quantiy) public void setsalary(doubly wweklysalary) public void setcommision(double itemcommision) public void setquantity(int totalsold) public double earnings()
Exempel : En gammal tentamensuppgift 1 a). Antag att länder kan vara antingen monarkier eller republiker. Statschefen i en monarki är en regent (dvs en kung eller regerande drottning), statschefen i en republik är en president. Alla monarkier och vissa republiker har ett parlamentariskt system med en regeringschef med titeln premiärminister, men i vissa republiker som ej har ett parlametariskt system (t ex USA) är presidenten även regeringschef. Modellera i Java med fyra klasser denna situation, som den dessutom beskrivs med nedanstående UML-liknade klassdiagram: 9p) Land String namn String statschef() String regeringschef() Monarki String regent String premiärminister String tostring() Republik String president String tostring() ParlamentariskRepublik String premiärminister String tostring() b) Skriv en klass med ett huvudprogram som skapar en array med tre länder och som skriver ut information om länderna. Utskriften skall bli så här: Sverige (monarki) Statchef : Kung Karl XVI Gustav. Regeringschef : Göran Persson USA (republik) Statchef och regeringschef : President William Clinton Frankrike (parlametarisk republik) Statchef : President Jaques Chirac. Regeringschef : Lionel Jospin Utskiften av uppgifterna om de tre länderna skall ske utan användning av instanceof och med metoderna tostring(). 3p) c) Arrayen i uppgift b) kan skapas och instansieras på två sätt. Hur ser det sätt ut som du inte använde i uppgift b)? 2p)
2 a) Ge ett exempel på dynamisk bindning av metoder i Java. 2p) Förslag till svar. public abstract class Land { // 1 a) protected String namn; public abstract String statschef(); public abstract String regeringschef(); public class Monarki extends Land{ private String regent; private String premiärminister; public Monarki(String inamn, String iregent, String ipm) { namn = inamn; regent = iregent; premiärminister = ipm; public String statschef(){ return regent; public String regeringschef() { return premiärminister; { return namn + " (monarki) \n Statchef : " + regent + ". Regeringschef : " + premiärminister; public class Republik extends Land{ protected String president; public Republik(String inamn, String ipresident) { namn = inamn; president = ipresident; public String statschef(){ return president; public String regeringschef() { return president; { return namn + " (republik) \n Statchef och regeringschef : " + president;
public class ParlamentariskRepublik extends Republik { private String premiärminister; public ParlamentariskRepublik(String inamn, String ipresident, String ipm) { super(inamn, ipresident); premiärminister = ipm; public String statschef(){ return president; public String regeringschef() { return premiärminister; { return namn + " (pralmetarisk republik) \n Statchef : " + president + ". Regeringschef : " + premiärminister; public class Lander { // 1 b) public static void main(string [] args) { Land [] land = {new Monarki("Sverige", "Kung Carl XVI Gustav", "Göran Persson"), new Republik("USA", "President William Clinton"), new ParlamentariskRepublik("Frankrike", "President Jaques Chirac", "Lionel Jospin"); for (int i = 0; i < land.length; i = i+1) { System.out.println(land[i]); Land [] land = new Land[3]; // 1c) land[0] = new Monarki("Sverige", "Kung Karl XVI Gustav", "Göran Persson"); land[1] = new Republik("USA", "President William Clinton"); land[2] = new ParlamentariskRepublik("Frankrike", "President Jaques Chirac", "Lionel Jospin"); // 2 a) Metoden tostring() i snurra i 1 b) är den metod som "hör ihop med" just den typ av objekt som är aktuellt just då.
Exempel : En gammal tentamensuppgift 1 a). Alla länder har ett namn och ett namn på sin valuta. Vissa länder är medlemmar i EU och blev medlemmar ett visst år. Vissa av EU-länderna har en gemensam valuta med namnet "euro". Vissa länder (t ex Sverige som är ett EU-land, Frankrike som är ett Euro-EU-Land och Norge som bara är ett Land, men inte Storbrittanien som är ett EU-land) är med i Shengen-samarbetet. Dessa länder har tillgång till ett polisregister register som dessa staters polismyndigheter kan söka i. Modellera i Java med sex klasser och ett gränsnitt denna situation, som den dessutom beskrivs med nedanstående UML-liknade klassdiagram. Samtliga klasser skall dessutom ha lämplig konstruktor som ger instanvariablerna lämpliga värden. 9p) Land - String namn //landets namn - String valuta //valutans namn Shengen public Object searchfor(string iperson) EULand - int inträdesår ShengenLand - Map register EuroEULand ShengenEULand - Map register ShengenEuroEULand - Map register Vänd! Tips: Implementeringen av metoden searchfor(string iperson) görs med metoden get i Map, se t ex Skansholm sid 498. Metoden deklareras alltså i gränsnittet Shengen,och skall implementeras av "Shengen-klasserna". Konstruktorn för Shengenländer har en parameter för att instansiera registret av typen Map. Euroländer får valutanamnet "euro" automatiskt utan medverkan av konstruktorerna för dessa sorts länder. b). Kan man i Java förenkla problemet i 1 a) genom att använda multipelt arv? 1p) c). Vilka av namnen Land, EULand, EuroEULand, Shengen, ShengenLand, ShengenEULand, ShengenEuroEULand och Map kan användas som typnamn? 1p)
d). Skulle man kunna ha en array där elementen skull kunna vara instanser av vilken som helst av de sex klasserna? Hur deklarerar man i så fall en sådan array, och hur tillfogas i så fall några länder, t ex Sverige och Frankrike och Norge till vektorn? 1p) e). Förse de av de sex klasserna där det behövs med en metod String tostring() som returnerar landets namn, namnet på valutan och i förekommande fall inträdesår i EU samt om landet är ett Shengenland. 3p) f). Skriv en metod void printinfo(vector länder) som med en slinga (snurra, loop) skriver ut information om alla länder i vectornländer med hjälp av tostring() i uppgift e. g) Förklara vad som menas med dynamisk bindning. 2p) Förslag till svar. public class Land { protected String valuta = ""; protected String namn = ""; public Land(String inamn, String ivaluta) { namn = inamn; valuta = ivaluta; { return (namn + ": valuta " + valuta); //1a, 1e public interface Shengen { public Object searchfor(string iperson); import java.util.*; public class ShengenLand extends Land implements Shengen { private Map register; public ShengenLand(String inamn, String ivaluta, Map iregister) { super(inamn, ivaluta); register = iregister; public Object searchfor(string iperson){ return register.get(iperson); { return super.tostring() + " i Shengen";
public class EULand extends Land { protected int inträdesår; public EULand(String inamn, String ivaluta, int iinträdesår) { super(inamn, ivaluta); inträdesår = iinträdesår; { return super.tostring() + " i Eu år " + inträdesår; import java.util.*; public class ShengenEULand extends EULand implements Shengen { private Map register; public ShengenEULand(String inamn, String ivaluta, int iinträdesår, Map iregister) { super(inamn, ivaluta, iinträdesår); register = iregister; public Object searchfor(string iperson){ return register.get(iperson); { return super.tostring() + " i Shengen"; public class EuroEULand extends EULand { public EuroEULand(String inamn, int iinträdesår) { super(inamn, "euro", iinträdesår);
import java.util.*; public class ShengenEuroEULand extends EuroEULand { private Map register; public ShengenEuroEULand(String inamn, int iordförandeår, Map iregister) { super(inamn, iordförandeår); register = iregister; public Object searchfor(string iperson){ return register.get(iperson); { return super.tostring() + " i Shengen"; Multipelt arv är inte tillåtet i Java. Det vore bekvämt här Alla // 1b // 1c import java.util.*; // public class TestLander { public static void main(string [] args) { Vector lv = new Vector(); //1d Map m = new Hashtable(); lv.add(new ShengenEULand("Sverige", "krona", 1995, m)); lv.add(new ShengenEuroEULand("Frankrike", 1955, m)); lv.add(new EULand("Storbrittanien","pund", 1965)); lv.add(new Land("Norge","krona")); printinfo(lv); public static void printinfo(vector v) { for(int i = 0; i < v.size(); i = i+1) { System.out.println(v.elementAt(i)); // 1f Beroende på objektets typ används korrekt version av en metod, t ex tostring() ovan. Detta fungerar eftersom metodrna hittas i minnet via objekten, som innehåller referenser till adekvat version //1g
Hemuppgifter redovisning v46. 1. CBA-hemuppgift : Implementera linjär-algebra-vektorer i planet med klassdefinitioner med en abstract klass Vector och två konkreta subklasser Cart och Polar för att kunna instansiera vektorena kartesiskt eller polärt. Man skall kunna göra de operationer på vektorer som vi kunde göra med klassen Vector i förra veckans hemuppgifter. Skriv om och komplettera testprogrammen från förra veckans hemuppgifter så att du kan testa våra nya klasser. Jag gjorde enligt följande UML-diagram: Vector public abstract double xproj(); public abstract double yproj(); public boolean equals(vector other) public Vector sum (Vector other) public Vector difference(vector other) public Vector scalarmultiple(double k) public double dotproduct(vector other) public double norm() Cart private double xcomp; private double ycomp; public Cart(public double ix, double iy) public double xproj() public double yproj() Polar private double length; private double fi; public Polar(double, ilength, double ifi ) public double xproj() public double yproj() public scalarmultiple(double k) Skapa lämpligen en egen katalog vectorinherit för denna uppgift. Enklast görs hemuppgiften genom att modifiera klassen Vector.java från tidigare hemuppgift.
2. CBA-hemuppgift : Början till "Vick-spelet". Ändra i Gadget från förra hemupgifterna så att det fungerar som förut när du kör MoveGadget med klasserna från denna veckas uppgift 1. 3. CBA-hemuppgift : Början till "Vick-spelet". Skriv en klass Board för att definera en kvadratisk yta med sidan 0.35 m. Förse Board med en metod public boolean outsideboard(vector p) som returnerar om p innanför ytan. Ändra i Gadget och MoveGadget så att ett förmål (Gadget) känner till vilken yta (Board) föremålet är placerad på och så att man återplacerar föremålet i startläge när föremålet kommer utanför ytan. Förlag till design (även din Vector-klass måste ju vara med, finns ej i klassdiagrammet) : MoveGadget public static void main(..) { Gadget gadget Board board Board private final double boardsize public boolean outsideboard(vector P) Gadget private Vector p private Vector v private Board board private static final double g = 9.81 public void placeon(board iboard) public void tick(double deltat, double alpha, double beta) public void resetat(vector ip, Vector iv) public Vector getwhere()
4. B-hemuppgift : Uppgift DD 9.14 om fyrhörningar. För att kolla att det fungerar kan det vara roligt (ingår i uppgiften) att rita upp de skapade fyrhörningarna. Följande två klasser vars källkod finns på /info/inda01/inherit/b kan användas för att rita olika fyrhörningar. Provkörning av min lösning kan göras med -..> java DrawQuadrilaterals i samma katalog.varje gång man trycker på vagnretur (return) på tangentbordet med markören i det aktiva terminalfönstret ritas en ny fyrhörning. Fyrhörningarna skapas med de konstruerare jag har i min lösning, men löser du uppgiften med andra konstruerare får man ju ändra programmet. Jag utnyttjar också att jag för alla sorters fyrhörningar har en metod Vector [] getcorner() som returerar de fyra hörnens läge. import java.io.*; import java.awt.*; import java.awt.event.*; public class DrawQuadrilaterals { public static void main (String[] argv) throws IOException { DrawingFrame f = new DrawingFrame(800); f.drawql(new Quadrilateral(new Cart(100, 100), new Cart(100, 200), new Cart(300, 450), new Cart(300, 270) )); System.in.read(); f.drawql(new Trapetzoid(new Cart(100, 100), new Cart(100, 200), new Cart(100, -10), 150)); System.in.read(); f.drawql(new Parallelogram(new Cart(100, 100), new Cart(100, 10), new Cart(10, 90))); System.in.read(); f.drawql(new Rectangle(new Cart(100, 100), new Cart(100, 10), 50)); System.in.read(); f.drawql(new Square(new Cart(100, 200), new Cart(100, 10))); System.in.read(); System.exit(0);
import java.io.*; import java.awt.*; import java.awt.event.*; public class DrawingFrame extends Frame { private Quadrilateral ql; public DrawingFrame(int ibredd) { setsize(ibredd, ibredd); setvisible(true); public void drawql(quadrilateral iql) { ql = iql; repaint(); public void paint(graphics g) { Vector [] c = ql.getcorner(); for(int i = 0; i < 4 ; i = i+1) { g.drawline((int) ((c[i]).xproj()), (int) ((c[i]).yproj()), (int) (c[(i+1) % 4]).xProj(), (int) (c[(i+1)%4]).yproj()); Skriv klasserna för de olika fyrhörningarna. Man skall förstås genom arv framhäva att en kvadrat är en rektangel som är en parallelogram som är en trapetsoid som är en fyrsiding. Försök att göra konstruerarna på så sätt att så lite information som möjligt används för att specifierade olika fyrsidingarna. Konstruerarna kan eventuellt använda sig av varandra med super.det framgår inte av boken om det är enbart fyrsidingarna som skall specifieras eller fyrsidingarna och deras orientering, men det blir generellast om man även kan specificera orienteringen. Min lösning antyds med klassdiagrammen på nästa sida, och vad de publika konstuerarna konstuerar hoppas jag framgår av diagrammen till höger. De "protected" konstruerarna kan behövas om man använder super.
Quadrilateral protected Vector [] corner = new Vector [4]; public Quadrilateral(Vector v0, Vector v1, Vector v2, Vector v3) protected Quadrilateral() vside1 public Vector [] getcorner() v1 v0 v3 v2 Trapetzoid v0 vside1 public Trapetzoid(Vector v0, Vector v1, Vector vside1, double lengthside2) protected Trapetzoid() v1 lengthside2 Parallelogram public Parallelogram(Vector v0, Vector vside1, Vector vside2) protected Parallelogram() vside2 v0 vside1 Rectangle lengthside2 public Rectangle(Vector v0, Vector vside1, double lengthside2) v0 vside1 Square public Square(Vector v0, Vector vside1) v0 vside1 vside1
5. A-hemuppgift : Början till Schack-spelet. Skriv klasser för ett arvsträd för schackpjäser enligt detta UML-liknade klassdiagram: Chessman protected boolean white; public abstract boolean legalmove(path p); public String tostring; public boolean iswhite() Queen public Queen(boolean iwhite) pub lic boolean legalmove(path p) King public King(boolean iwhite) pub lic boolean legalmove(path p) Bishop public Bishop(boolean iwhite) pub lic boolean legalmove(path p) Rook public Rook(boolean iwhite) pub lic boolean legalmove(path p) Pawn Knight public Knight(boolean iwhite) pub lic boolean legalmove(path p public Pawn(boolean iwhite) pub lic boolean legalmove(path p) Metoden legalmove(path p) skall returnera om draget p är ett korrekt schackdrag för denna sorts schackpjäs (på ett tom bräde). Metoden tostring() retunerar t ex Kung för objekt av klassen King osv. Pjäser "vet" om de är vita eller svarta, och iswhite() frågar pjäser om detta. Klassen Path skrevs i hemuppgifterna förra veckan. Skriv ett testprogram som kan mata in drag gång på gång (vidareutveckla testprogrammet från förra veckan) och skriver ut om draget är korrekt för de sex olika sorternas pjäser. Min lösning som vanligt på /info/inda01/inherit/a.