jonas.kvarnstrom@liu.se 2015 TDDD78 Viktiga begrepp i programmering / objektorientering
Repetition 3 En variabel består av: Ett symboliskt namn i programkoden En lagringsplats i minnet, som kan innehålla variabelns värde Python: längd = 10 höjd = 5 hälsning = "hello" färger = [red, green] Programkod längd höjd hälsning färger Minne vid körning 10 5 "Hello" [red, green]
Minnesadresser 4 Programkod Minne vid körning Varje värde "startar" på en specifik minnesaddress Minnesadresser hanteras "bakom kulisserna" "int längd = 10;" "lagra 32-bitars heltal 10 på adress 10000 10003" int: längd 10 10000 int: höjd 5 10004 String: hälsning List: färger "Hello" [red, green] 40000 50000
Minnesadresser 2 5 Programkod Minne vid körning I vissa språk kan man få veta minnesadresserna C: höjd == 5 &höjd == 10004 ("adressen till höjd") int: längd 10 10000 int: höjd 5 10004
Pekare (1) 6 Programkod Minne vid körning En variabel av pekartyp kan innehålla en adress C: "pekare till int" heter "int*" int* höjdpekare = &höjd; Värdet blir adressen 10004 int: längd int: höjd 10 5 10000 10004 int*: höjdpekare 10004 40000 Värdet 10004 är adressen till ett annat värde i minnet
Pekare (2): Tilldelning 7 Programkod Minne vid körning Ändra pekarens värde ändra vart den pekar C: int* höjdpekare = &höjd; höjdpekare = &längd; int: längd 10 10000 int: höjd 5 10004 int*: höjdpekare 10000 40000
Pekare (3): Ändra i utpekat värde 8 Programkod Minne vid körning För att ändra i värdet på den utpekade adressen krävs alltså annan syntax C: int* höjdpekare = &höjd; *höjdpekare = 14; int: längd 10 10000 int: höjd 5 14 10004 int*: höjdpekare 10004 40000 "*höjdpekare = 14" definieras som "läs pekarens värde (10004), lagra 14 på den adressen"
Pekare i Java 10 Namn Värde i minnet I Java är en objektvariabel alltid en pekare! Circle mycircle = new Circle(10, 20, 30); Circle: mycircle 49152 10000 2. Skapa variabeln, låt den peka på obj. Object (data) header: x 10 y 20 r 30 49152 1. Skapa objektet (minne, konstruktor, ) mycircle är pekaren (4/8 bytes), inte objektet! Java gömmer dess numeriska värde (irrelevant för vår kod): Vi kan inte få fram talet 49152
Pekare i Java (2): Exempel 11 Namn Circle: c1 Värde i minnet 10000 Två cirklar: Circle c1 = new Circle(1,1,1); Circle c2 = new Circle(2,2,2); 5000 Circle: c2 Object (data) header: x 1 y 1 r 1 20000 10000 11000 Object (data) header: x 2 y 2 r 2 20000
Pekare i Java (3): Tilldelning 12 Namn Circle: c1 Circle: c2 Värde i minnet 10000 Object (data) header: x 1 y 1 r 1 10000 5000 10000 11000 c2 = c1; c1, c2 är pekare Tilldela c2 värdet av c1 sätt c2 till 10000 ändra vart c2 pekar Kommer inte att kopiera själva cirkeln, fält för fält! Object (data) header: x 2 y 2 r 2 20000 Kvarvarande objekt som ingen pekar på inte ett problem i Java (skräpsamling)
Pekare i Java (4): Ändra i utpekat värde 13 Namn Circle: mycircle Värde i minnet 49152 Kan ändra i värdet (objektet) som lagras på utpekad adress Ändrar aldrig hela objektet, alltid en medlem i taget Java: objekt punkt medlem mycircle.x = 4711; 10000 Object (data) header: x 4711 y 20 r 30 49152 mycircle.x x-värdet i det objekt som pekas ut av mycircle
Pekare i Java (5): Tilldelning 14 Namn Circle: c1 Värde i minnet 10000 5000 c2.x = 100; Nu är c1.x också 100! Circle: c2 Object (data) header: x 100 y 1 r 1 10000 10000 11000
Exempel: Trädstruktur 15 Trädstruktur: Nod 1 Nod 2 Nod 3 Nod 4 Nod 5 Alla noder ska veta vem föräldern är Kan inte innehålla föräldern men kan ha en pekare till den! class TreeNode { String name; // Pekare till en sträng TreeNode parent; // Pekare till en föräldernod
Exempel: Trädstruktur (2) 16 Nod 1 Minne name: Nod 1 10000 Nod 2 Nod 3 name: Nod 2 49152 parent: 10000 Med pekare: Många kan peka på samma föräldernod trots att noden bara lagras en gång name: Nod 3 parent: 10000 53284
Exempel: Trädstruktur (3) 17 Men nod 1 har ju ingen förälder! Vad ska parent ha för värde? Minne name: Nod 1 parent:??? 10000 name: Nod 2 49152 parent: 10000 name: Nod 3 53284 parent: 10000
Null-pekare 18 Alla objektpekare kan ha specialvärdet null Pekar "ingenstans" "inte applicerbart": Noden har ingen förälder "vi vet inte än" Minne name: Nod 1 parent: null 10000 Representeras ofta internt som adressen 0 Spelar ingen roll för oss I Java ser vi bara värdet null name: Nod 2 parent: 10000 49152 Med pekare: Kan lätt ange avsaknad av värde name: Nod 3 parent: 10000 53284
Null-pekare Vad kan man göra om pekarens värde är null? 19 Använda själva pekarvariabeln if (this.parent == other.parent) // Jämför pekarnas värden (0 == 49152), // tittar inte efter något objekt // (Motsvarar operatorn "is" i Python) painter.draw(circle1) // Om circle1 == null, // får parametern till draw också värdet null Inte använda fälten och metoderna i objektet den pekar på! Den pekar ju inte på något objekt! this.parent = null; this.parent.name = "Hello"; // OK // Fel vid körning: NullPointerException parent:
Sammansättning 1 Anta att vi har en Point-klass: public class Point { private double x, y; public Point(double x, double y) { this.x = x; this.y = y; public double getx() { return x; public double gety() { return y; public double getdistfromorigin() { return Math.sqrt(x*x + y*y); 21
Sammansättning 2 Nu vill vi skapa en cirkelklass två alternativ: public class Circle { // Alla fält är primitiva typer, som tidigare private double x, y, r; public Circle(double x, double y, double r) { this.x = x; this.y = y; this.r = r; Implementera allt från början class Circle { // Fält kan vara objekt private Point center; private double r; public Circle(Point center, double r) { this.center = center; this.r = r; Använd existerande punktklassen! Sammansättning = composition 22
Sammansättning 3 Med sammansättning: En cirkel har en punkt, eller består av en punkt (och en radie) Återanvändning av existerande kod (Point kunde vara komplicerad) Mindre upprepning bra! 23 Exempel i labben: Listor finns redan En kö har en lista där den kan lagra sina element Använd existerande punktklassen! class Circle { // Fält kan vara objekt private Point center; private double r; public Circle(Point center, double r) { this.center = center; this.r = r;
Sammansättning och delegering 24 Om man vill ge tillgång till "komponentens" funktionalitet: Delegera! Använd existerande punktklassen! class Circle { // Fält kan vara objekt private Point center; private double r; public Circle(Point center, double r) { this.center = center; this.r = r; public double getdistfromorigin() { return center.getdistfromorigin(); Vad är cirkelns avstånd till origo? Samma som punktens!
Sammansatt objektstruktur 1 I vissa språk: Sammansatta objekt är sammansatta i minnet class Circle { Point center; double radius; Circle c1 = new Circle(); 25 c1 Object header: x y (data) radius 0.0 0.0 "Point-delen" 0.0 av en cirkel
Sammansatt objektstruktur 2 Men javas objektvariabler är alltid pekare! Ett "sammansatt" objekt består alltid av flera fullständiga objekt class Circle { En Circle består av Point center; en Point-pekare (inte en Point) double radius; en double Circle c1 = new Circle(); 26 c1 Object header: center radius (data) 0.0 Object header: x y (data) 0.0 0.0
Sammansatt objektstruktur 3 Detta har alla konsekvenser som vi såg för pekare tidigare Exempel: Två cirklar kan ha samma centrumobjekt Point center = new Point(10, 20); Circle c1 = new Circle(center, 7); Circle c2 = new Circle(center, 12); 27 c1 Object header: center radius (data) 7.0 Object header: x y (data) 10.0 20.0 c2 Object header: center radius (data) 12.0
Vad ska {klassen, metoden, fältet uppfylla?
Kontrakt Kontrakt: Överenskommelse som anger Vad som ska tillhandahållas Vad som förväntas i utbyte Allmänna regler runt utbytet 29 Inom objektorienterad programmering: Vilka värden kan en metod ta emot? Vad garanterar metoden att den gör, om den får sådana värden? Vad returnerar den? Vad garanterar en klass angående sitt tillstånd och beteende?
Kod kan innehålla (vissa) formella kontrakt 30 class Circle { private double x, y, r; public Circle(double x, double y, double r) { this.x = x; this.y = y; this.r = r; public double getarea() { return Math.PI * this.r * this.r ; Krav på input: Parametrarna måste vara av typ double Löfte om resultattyp: Returnerar alltid double Följer direkt från manifest typning Så vanligt att man oftast inte ens ser det som kontrakt
Varför räcker inte koden? 31 class Circle { private double x, y, r; public Circle(double x, double y, double r) { this.x = x; this.y = y; this.r = r; public double getarea() { return Math.PI * this.r * this.r ; + Koden definierar exakt vad programmet gör! Varför räcker inte detta?
Varför räcker inte koden? (2) 32 class Circle { private double x, y, r; public Circle(double x, double y, double r) { this.x = x; this.y = y; this.r = r; public double getarea() { return Math.PI * this.r * this.r ; Svårt att inse alla konsekvenser av tusentals rader kod + Kontraktet bör sammanfatta vad som garanteras, krävs
Varför räcker inte koden? (3) Vi är inte intresserade av vad koden gör! Mycket viktig konceptuell skillnad mellan: 33 Vad vi faktiskt gör (koden) Implementationsdetaljer Implementationsdetaljer Vad vi lovar att göra, och att fortsätta göra i all evighet (kontraktet) Buggar Buggar
Exempel 1: Sortering Exempel: Sortera strängar Input: [diverse, test, input, något, sortera] 34 Nu vill vi sortera: Primärt efter längd Sekundärt, om orden är lika långa i bokstavsordning Vi vill alltså få: [test, input, något, diverse, sortera] 5 tecken 7 tecken bokstavsordning inom varje grupp
Exempel 1: Metod 35 En metod: Börja med en lista ord [diverse, test, input, något, sortera] Sortera i bokstavsordning utan att tänka på längd [diverse, input, något, sortera, test] Sortera i längdordning utan att tänka på bokstäverna Generellt finns 4 möjliga resultat: [test, input, något, diverse, sortera] [test, något, input, diverse, sortera] [test, input, något, sortera, diverse] [test, något, input, sortera, diverse] Vi tänker bara på längden Ord med samma längd kan kastas om Med stabil sortering: Byt bara ordning på två element om det krävs [test, input, något, diverse, sortera] input fortfarande före något diverse fortfarande före sortera Löser vårt problem!
Exempel 1: Vad kan vi se i koden? Anta att en lista kan sortera sig Men det finns inget uttryckligt kontrakt måste läsa koden class List { void sort() { for (int i=0; i < strings.length-1; i++){ for (int j=1; j < strings.length-i; j++){ if (strings[j-1].compareto(strings[j]) > 0) { String temp = strings[j-1]; strings[j-1] = strings[j]; strings[j] = temp; 36 Aha sort() kör bubble sort, som är stabil! Då utnyttjar vi det!
Exempel 1: Vad är tillåtet? Senare: Någon vill förbättra listklassen! Bubbelsortering är ineffektivt 37 class List { Om vi har detta Får vi skriva om så här? class List { void sort() { // Utför bubble sort // (råkar vara stabil) void sort() { // Utför heap sort // (snabbare, men inte stabil) // Andra kanske förlitar sig på egenskaper koden "råkade" ha dilemma!
Kontrakt 1 Undvik genom kontrakt! Vissa språk har formellt stöd t.ex. Eiffel set_hour (new_hour: INTEGER) -- Set `hour' to `new_hour' require valid_argument: 0 <= new_hour and new_hour <= 23 do hour := new_hour ensure hour_set: hour = new_hour end 38 precondition postcondition Ofta får man istället använda dokumentationen Idealet: Bara det som står i kontraktet gäller titta aldrig på koden! class List { /** Sorterar listan efter ordlängd. */ void sort() { Här står inget om stabilitet. Då kan vi inte förutsätta det!
Kontrakt 2 Ju tydligare, desto bättre 39 class List { /** Sorterar listan efter ordlängd. Stabilitet garanteras inte. */ void sort() { Beskriv gärna mer om vad koden lovar och inte lovar
Exempel 2: Vad utlovas? Får jag ("Cirkel-användare") skicka in en negativ radie här? Ser inget som skulle orsaka problem Men vem vet hur klassen kan ändras i framtiden? 40 class Circle { double x, y, r; Circle(double x, double y, double r) { this.x = x; this.y = y; this.r = r;
Exempel 2: Vad är tillåtet? Får jag ("Cirkel-utvecklare") ändra min kod så här? Verkar rimligt att cirklar ska ha positiv radie Men kod som förut fungerade kommer nu att krascha! 41 class Circle { double x, y, r; class Circle { double x, y, r; Circle(double x, double y, double r) { this.x = x; this.y = y; this.r = r; Circle(double x, double y, double r) { if (r <= 0.0) throw ; this.x = x; this.y = y; this.r = r;
Exempel 2: Kontrakt? Med kontrakttänkande: 42 class Circle { double x, y, r; /** Skapar en cirkel med angivna koordinater och radie. */ Circle(double x, double y, double r) { this.x = x; this.y = y; this.r = r; Metoden tar emot double. Det står inget om begränsningar. Alltså lovar den att klara godtycklig double.
Exempel 2: Kontrakt? Med kontrakttänkande: 43 class Circle { double x, y, r; /** Skapar en cirkel med angivna koordinater och radie. Radien måste vara strikt positiv. */ Circle(double x, double y, double r) { this.x = x; this.y = y; this.r = r; Lovar att klara strikt positiva radier. Kräver sådan input. Skickar du in annat är beteendet odefinierat (kan krascha, )
Grundregel för kontrakt: Dokumentera alltid vad koden kräver och vad den lovar
Relaterad grundregel: Dokumentera inte vad koden gör utan vad den ska göra (och gärna varför) Vi återkommer till kontrakt och löften flera gånger!
API API: Application Programming Interface Specifikation av hur en applikation kan använda / kommunicera med t.ex. operativsystemet, andra program, klassbibliotek, osv. 47
Javas klassbibliotek 48 Javas standardiserade klassbibliotek "Java Standard Edition API" 4241 färdiga klasser inom många områden Standardiserade fält, metoder,
Dokumentation Dokumentation: http://docs.oracle.com/javase/8/docs/api/ Skriven för att kunna implementeras igen av någon annan, bara genom att utgå ifrån beskrivningen! API-beskrivningen ska vara ett fullständigt kontrakt för exakt vad som krävs av varje klass och metod 49
Annoteringar 1 51 Annotateringar låter oss addera metadata till deklarationer Metadata inte kod, utan information om kod Associeras med klasser, metoder, variabler, parametrar, import javax.annotations.generated; public class MyGUI { @Generated private JButton closebutton; @Generated private void setup() { Medlemmarna har genererats av ett verktyg (t.ex. en GUI-byggare)
Annoteringar 2 Exempel: Kod som är deprecated Gammal, föråldrad, bör inte användas längre Deprecate = uttrycka ogillande 52 @Deprecated void deprecatedmethod() { Används metoden ändå: Kompilatorn kan varna void usemeinstead() {
Annoteringar 3 53 Vissa annoteringar kan definiera eller stärka formella kontrakt import org.jetbrains.annotations.notnull; import org.jetbrains.annotations.nullable; public class TimeTools { @NotNull public static String gettimestring() { return ; Metoden kan inte returnera null Används av dataflödesanalys, påverkar varningar public static String gettimestring(@nullable Date date) { return ; Parametern får vara null metodkoden måste ta hand om null-värden
(access modifiers)
Bakgrund Tänk er en lista som garanterar att elementen är i sorterad ordning! 55 class SortedList { String[] elements; int size; String get(int pos) { return void add(string el) { int pos = findnewpositionfor(el); insertat(pos, el); Lagring för alla element Hämtar element Stoppar in element på rätt plats int findnewpositionfor(string el) { void insertat(int pos, String el) { med dessa hjälpmetoder
Att gömma information Ofta vill vi gömma delar av klasser och objekt 56 class SortedList { String[] elements; int size; int findnewpositionfor(string el) { void insertat(int pos, String el) { String get(int pos) { return Syns bara inuti själva klassen void add(string el) { int pos = findnewpositionfor(el); insertat(pos, el); Synligt utåt
Gömma: För att inte förvirra användarna Mindre synligt mer överskådligt 57 class SortedList { String[] elements; int size; int findnewpositionfor(string el) { void insertat(int pos, String el) { String get(int pos) { return Användaren behöver aldrig anropa findnewpos eller insertat Att de syns förvirrar, gör klassen oöverskådlig! void add(string el) { int pos = findnewpositionfor(el); insertat(pos, el); Detta måste användarna känna till för att använda klassen
Gömma: Minska åtaganden (kontrakt) Det vi har visat upp, måste vi normalt ha kvar 58 class SortedList { String[] elements; int size; int findnewpositionfor(string el) { void insertat(int pos, String el) { String get(int pos) { return Användarna behöver inte komma åt element via list.elements! Göm det enbart get/add används vi kan byta ut resten om vi vill frihet! void add(string el) { int pos = findnewpositionfor(el); insertat(pos, el); Detta ska utnyttjas av användarna, måste visas och finnas kvar
Gömma: Minska åtaganden (kontrakt) Det vi har visat upp, måste vi normalt ha kvar 59 class SortedList { String[] elements; int size; int findnewpositionfor(string el) { void insertat(int pos, String el) { Användarna behöver inte komma åt element via list.elements! Göm det enbart get/add används String get(int class SortedList pos) { return { vi kan byta ut resten om vi vill! ListNode first; void add(string ListNode el) findnewpositionfor(string { el) { Detta ska utnyttjas int pos = findnewpositionfor(el); void insertelementat(listnode pos, String el) { av användarna, insertat(pos, String el); get(int pos) { return måste visas void add(string el) { och Den finnas "publika" kvardelen ListNode pos = findnewpositionfor(el); insertelementat(pos, el); har kvar samma signatur
Gömma: För att uppfylla vårt kontrakt 60 /** * This class maintains a sorted list of elements. * As long as the contents of an element does not change, * elements will always be in sorted order. */ class SortedList { String[] elements; int size; int findnewpositionfor(string el) { void insertat(int pos, String el) { void add(string el) { int pos = findnewpositionfor(el); insertat(pos, el); Om användare kan anropa insertat med fel position blir listan osorterad Då bryter vi vårt löfte!
Hur gömmer vi information?
Att gömma data i Java Många OO-språk tillåter oss att gömma data I Java kan klasser, gränssnitt, fält och metoder vara: public alla har tillgång protected [nothing] private tillgång i underklasser + andra i samma paket tillgång i samma paket (används sällan) tillgång inom samma klass 62 public class SortedList { private String[] elements; private int size; private int findnewpositionfor(string el) { private void insertelementat(int pos, String el) { public void add(string el) { int pos = findnewpositionfor(el); insertelementat(pos, el); Vad hade private betytt utan OO? Vi lovar mindre i vårt kontrakt! Skapa ett stabilt gränssnitt för "allmänheten": add() Göm all info om implementationsdetaljer
Inkapsling 63 Två betydelser hos Inkapsling (Encapsulation) Koppla ihop data och funktioner Fundamentalt i OO Objekt: en kapsel som innehåller både data och funktioner Begränsa tillgång till medlemmar Genom accessmodifierare Vissa medlemmar är instängda i en ogenomskinlig kapsel, och kan inte ses eller utnytttjas från utsidan
Privata fält 64 Vanlig princip: Fält är implementationsdetaljer, ska vara privata Om det verkligen är rimligt att andra klasser ska komma åt fältvärden: Skapa en public accessor-metod (getter) vid behov Skapa en public mutator-metod (setter) vid behov Kan ändra intern representation: Ändra bara getter/setter, som är i samma klass private double diameter; /** Set the radius to r (must not be negative). */ public void setradius(final double radius) { if (r >= 0.0) diameter = 2 * radius; else throw new IllegalArgumentException("Negative radius: " + r); Kan lägga till konsistenskontroll
Namngivning: Getter, Setter Namngivning: 65 private double radius; private boolean visible; /** Set the radius to r (must not be negative). */ public void setradius(final double r) { if (radius >= 0.0) radius = r; else throw new ; public double getradius() { return radius; public boolean isvisible() { return visible; Setters: void setproperty( ) Getters: getproperty() Booleska getters: isproperty() Mer info: http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html
Tillåtna publika fält Vissa anser att det finns undantag Rena datastrukturer (inte mycket beteende, osannolikt att det skulle ändras i framtiden) 66 public class Dimension { public int width; public int height;
Tre sorters variabel 68 Lokal variabel: Deklareras i en metod Varje metodanrop har sin egen "kopia" Två anrop till foo() egna värden på x Anropet avslutas variabeln frigörs Fält: Deklareras i en klass Varje instans (objekt) har sin egen "kopia" Två olika objekt egna värden på x Två anrop samma x! Objektet slängs bort variabeln frigörs Statiskt fält: I en klass, static Varje programkörning har sin egen "kopia" Ett enda värde lagras, i klassen istället för i ett objekt public class MyProg { public void foo() { int x = calcx(); public class MyProg { int x; public void foo() { drawat(x); public class MyProg { static int x; public static void foo() { print(x); Inte ett intuitivt nyckelord! Olika lagring, olika livstider!
Statiska fält 1: Namngivna konstanter Konstanter bör (nästan) alltid namnges Jämför läsbarhet: for (int i = 1; i <= 52; i++) { Alternativt: for (int i = 1; i <= decksize; i++) { Mycket viktigt för programunderhåll! 69
Statiska fält 2: Namngivna konstanter 70 Namngivna konstanter läggs oftast på klassnivå (static) public class Circle { public static final double PI = 3.1415926535897932384626434; Circle c1 = new Circle(1, 2, 3); Circle c2 = new Circle(10,11,12); Namngivna konstanter: static i klassen final ändras inte namn stora bokstäver (WITH_UNDERSCORE) c1 c2 Header: (data) x 1 y 2 r 3 Header: (data) x 10 y 11 r 12 jonkv.graphics.circle Static field PI getcircum getarea setradius 3.14159 code code code Lagras en gång slösar inte utrymme
Statiska fält 3: "Globala" variabler Kan också användas till "globala" variabler, åtkomliga överallt 71 Varning! Globala variabler ger ofta problem! Använd dem bara i undantagsfall!
Statiska fält 4: Exempel Exempel: Hålla reda på antal cirklar som skapats c1 c2 public class Circle { public static final double PI = 3.14159; // constant pi public static int circlescreated = 0; // how many are created? public double x, y, r; public Circle(double x, double y, double radius) { this.x = x; this.y = y; this.r = radius; circlescreated++; Circle c1 = new Circle(1, 2, 3); Circle c2 = new Circle(10,11,12); Header: (data) x 1 y 2 r 3 Header: x y (data) 10 11 jonkv.graphics.circle Static field circlescreated Static field PI getcircum getarea setradius code code code 72
Statiska fält 5: Att referera till dem För att referera till ett statiskt fält: public class Circle { public static final double PI = 3.14159; public static int circlescreated = 0; public double x, y, r; public Circle(double x, double y, double radius) { this.x = x; this.y = y; this.r = radius; circlescreated++; // constant pi // how many are created? Här räcker "circlescreated", eftersom fältet är i samma klass 73 public class CircleTest { public static void main(string[] args) { System.out.println("Circles created: " + Circle.circlesCreated); System.out.println("Pi is: " + Circle.PI); Här använder vi klassnamn.fält
Statiska fält 6: Inte så här För att referera till ett statiskt fält: Inte så här: Circle c = new Circle(); System.out.println("Circles created: " + c.circlescreated); 74 Missvisande: Ser ut som att circlescreated är ett "vanligt" instansfält!