Oefeningen
Je vindt een IntelliJ project met de projectstructuur en tests voor alle oefeningen in volgende GitHub repository:
Klasse of record?
Geef enkele voorbeelden van types die volgens jou best als record gecodeerd worden, en ook enkele types die best als klasse gecodeerd worden.
Kan je, voor een van je voorbeelden, een situatie bedenken waarin je van record naar klasse zou gaan, en omgekeerd?
Sealed interface
Kan je een voorbeeld bedenken van een nuttig gebruik van een sealed interface?
Definieer een Email-record dat een geldig e-mailadres voorstelt.
Het mail-adres wordt voorgesteld door een String.
Controleer de geldigheid van de String bij het aanmaken van een Email-object:
- de String mag niet null zijn (anders
NullPointerException) - de String moet exact ƩƩn @-teken bevatten (anders
IllegalArgumentException) - de String moet eindigen op “.com” of “.be” (anders
IllegalArgumentException)
Notitie
De echte regels voor een geldig emailadres zijn uiteraard veel complexer. Zie bijvoorbeeld de voorbeelden van geldige e-mailadressen op deze Wikipedia-pagina.
Rechthoek
Schrijf een record die een rechthoek voorstelt. Een rechthoek wordt gedefinieerd door 2 punten (linksboven en rechtsonder). Gebruik een Coordinaat-record om deze hoekpunten voor te stellen. Zorg ervoor dat enkel geldige rechthoeken aangemaakt kunnen worden (dus: het hoekpunt linksboven ligt zowel links als boven het hoekpunt rechtsonder).
Voeg extra methodes toe:
- om de twee andere hoekpunten (linksonder en rechtsboven) op te vragen
- om na te gaan of een gegeven punt zich binnen de rechthoek bevindt
- om na te gaan of een rechthoek overlapt met een andere rechthoek. (Hint: bij twee overlappende rechthoeken ligt minstens ƩƩn hoekpunt van de ene rechthoek binnen de andere)
Expressie-hierarchie
Maak een set van records om een wiskundige uitdrukking voor te stellen.
Alle records moeten een sealed interface Expression implementeren.
De mogelijke expressies zijn:
- een
Literal: een constante getal-waarde (een double) - een
Variable: een naam (een String), bijvoorbeeld “x” - een
Sum: bevat twee expressies, een linker en een rechter - een
Product: gelijkaardig aan Som, maar stelt een product voor - een
Power: een expressie tot een constante macht
De veelterm \( 3x^2+5 \) kan dus voorgesteld worden als:
Maak nu een klasse ExpressionUtils met volgende statische methodes (de beschrijving volgt hieronder).
Gebruik pattern matching (en TDD) voor elk van volgende opdrachten:
- de methode
evaluatemoet de gegeven expressie evalueren voor de gegeven waarden van de variabelen. Bijvoorbeeld, \( 3x^2+5 \) evalueren met \( x=7 \) geeft \(152\). De parametervariableValuesbevat een mapping van variabelen naar hun toegekende waarde. - Schrijf de methode
prettyPrintdie de gegeven expressie omzet in een String, bijvoorbeeldprettyPrint(poly)geeft(3.0) * ((x)^2.0) + 5.0. Maak je op dit moment nog geen zorgen over onnodige haakjes. Hint: voor het pretty-printen van een som, pretty-print je eerst de linker- en rechterterm afzonderlijk. - Zorg er nu voor dat er geen onnodige haakjes verschijnen in het resultaat van
prettyPrint, door rekening te houden met de volgorde van de bewerkingen. (Hint: geef elke expressie een numerieke prioriteit) - (uitdagend) De methode
simplifymoet de gegeven expressie te vereenvoudigen door enkele vereenvoudigingsregels toe te passen. Bijvoorbeeld, het vervangen van \(3 + 7\) door \(10\), vervangen van \(x+0\), \(x*1\), en \(x^1\) door \(x\); vervangen van \(x * 0\) door \(0\), … - (uitdagend) de methode
differentiatemoet de afgeleide berekenen van de gegeven expressie in de gegeven variabele (bv. \( \frac{d}{dx} 3x^2+5x = 6x+5 \)). Geef het resultaat zo eenvoudig mogelijk terug (Hint: gebruiksimplify).
Denkvraag
Wat is het voor- en nadeel van het gebruik van pattern matching tegenover het gebruik van overerving en dynamische binding?
Met andere woorden, wat is het verschil met bijvoorbeeld de methodes simplify(), evaluate(), … in de interface Expression zelf te definiĆ«ren, en ze te implementeren in elke subklasse?
Extra oefeningen
Money
Maak een Money-record dat een geldbedrag (bijvoorbeeld 20) en een munteenheid (bijvoorbeeld “EUR”) bevat.
Voeg ook methodes toe om twee geldbedragen op te tellen. Dit mag enkel wanneer de munteenheid van beiden gelijk is; zoniet moet er een exception gegooid worden.
Interval
Maak een Interval-record dat een periode tussen twee tijdstippen voorstelt, bijvoorbeeld voor een vergadering. Elk tijdstip wordt voorgesteld door een niet-negatieve long-waarde.
Het eind-tijdstip mag niet voor het start-tijdstip liggen.
Voeg een methode toe om na te kijken of een interval overlapt met een ander interval. Intervallen worden beschouwd als half-open: twee aansluitende intervallen overlappen niet, bijvoorbeeld [15, 16) en [16, 17).
Programmeertaal
Breid de expressies uit de oefening hierboven uit tot je eigen mini-programmeertaal met interpreter. Voorzie daarvoor een sealed interface Statement met volgende klassen en betekenis:
Assign(name, expr): evalueerexpren sla het resultaat op in de variabelenamePrint(expr): evalueerexpren print de waarde uitIf(cond, thenBranch, elseBranch): evalueer expressiecond; indien dit 0 is, voer statementthenBranchuit, anders statementelseBranchWhile(cond, body): voer statementbodyuit zolang expressiecondnaar 0 evalueertSequence(stmts): voer een lijst van statementsstmts(een ‘blok’) na elkaar uit
Voeg dan een klasse Interpreter toe met een methode execute(Statement st) die het meegegeven statement (programma) uitvoert.
In je Interpreter maak je best gebruik van een klasse die de huidige toestand van het programma bijhoudt, met onderstaande interface:
Een voorbeeld van het gebruik van je interpreter: