• Een child-object past in een variabele of lijst van het parent-type. Via die variabele kan je enkel wat in de parent staat; welke override draait, beslist het echte object (uitleg).
  • Staat een methode virtual of abstract in de parent, roep ze dan gewoon op. Een is-test per soort is net wat polymorfisme overbodig maakt (uitleg).
  • Iets wat enkel een child heeft, bereik je met pattern matching: if (dier is Koe koe) koe.GeefMelk(); test en zet om in één keer (uitleg).
  • Een cast met haakjes zoals (Koe)dier crasht als het geen koe is. as geeft dan null: controleer daarop voor je verdergaat (uitleg).

Voorspel de uitvoer: wie neemt het woord? (Essential)

Eerste les in de aula. Je project heet Aula en heeft drie klassen, elk in een eigen bestand:

internal class Persoon
{
    public string Naam { get; set; }

    public virtual void StelVoor()
    {
        Console.WriteLine($"Ik ben {Naam}.");
    }

    public virtual void Groet()
    {
        Console.WriteLine("Hallo.");
    }
}
internal class Student : Persoon
{
    public override void StelVoor()
    {
        Console.WriteLine($"Ik ben {Naam}, eerstejaars.");
    }
}
internal class Lector : Persoon
{
    public string Vak { get; set; }

    public override void StelVoor()
    {
        Console.WriteLine($"Ik ben {Naam} en ik geef {Vak}.");
    }

    public void Groet()
    {
        Console.WriteLine("Goeiemorgen, aula!");
    }
}

Deel 1. Schrijf op papier wat dit programma toont, lijn per lijn. Typ de code pas daarna over en vergelijk.

Persoon vooraan =
    new Lector() { Naam = "Peeters", Vak = "programmeren" };
Persoon achteraan = new Student() { Naam = "Lotte" };
vooraan.StelVoor();
achteraan.StelVoor();
vooraan.Groet();
Lector peeters =
    new Lector() { Naam = "Peeters", Vak = "programmeren" };
peeters.Groet();
Console.WriteLine(vooraan is Persoon);
Console.WriteLine(vooraan is Lector);
Console.WriteLine(achteraan is Lector);
Lector lector = achteraan as Lector;
Console.WriteLine(lector == null);
Console.WriteLine(achteraan.GetType().Name);
if (vooraan is Lector l && l.Vak == "programmeren")
{
    Console.WriteLine("Dat wordt C#.");
}

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

Deel 2. Elk van deze vijf stukjes komt apart onderaan het programma uit deel 1. Compileert het niet, crasht het tijdens het uitvoeren, of werkt het? En waarom?

Console.WriteLine(vooraan.Vak);              // A
object iemand = achteraan;                   // B
iemand.StelVoor();
Lector nieuw = new Persoon();                // C
if (!achteraan is Lector)                    // D
{
    Console.WriteLine("Geen lector.");
}
Lector fout = (Lector)achteraan;             // E

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

Deel 3. De compiler gaf bij Lector een waarschuwing. Welke? Herstel ze. Wat toont de derde regel van deel 1 nu?

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

  • Het type van de variabele bepaalt wat je mag oproepen. Het echte object bepaalt welke override draait, maar enkel als de methode virtual of abstract is én de child override schrijft.
  • Een compileerfout zie je meteen. Een foute cast zie je pas als het programma loopt, en dan crasht het. as en is vermijden die crash.
  • Zie Polymorfisme, Het as keyword en Volgorde van bewerkingen met is en as.

Een eigen huis (Essential)

In UML naar code (hoofdstuk 15) maakte je een Huis dat zijn kamers zelf aanmaakt. Nu krijgt elke kamer een prijs, en kan een huis zoveel kamers krijgen als je wil.

  • Een Kamer heeft een Oppervlakte (in vierkante meter, een int), een Naam en een Prijs. Prijs heeft enkel een get, zonder instantievariabele erachter: de get geeft gewoon een getal terug of rekent het uit. Een gewone kamer kost 400 euro. Childs van Kamer overschrijven Prijs.
  • Maak minstens deze childs:
    • Badkamer: kost 500 euro.
    • Gang: kost 10 euro per vierkante meter.
    • Salon: kost 300 euro, of 500 euro als er een schouw is. Of er een schouw is, houd je bij in een bool-property HeeftSchouw.
  • Een Huis heeft een private lijst van kamers en deze methoden:
    • VoegKamerToe: voegt de kamer toe die je meegeeft.
    • BerekenPrijs: geeft de totale prijs van alle kamers terug.
    • ToonOverzicht: toont van elke kamer de naam en de prijs, en daaronder het totaal.

Testcode. Je code werkt als je exact deze uitvoer krijgt.

Huis huis = new Huis();
huis.VoegKamerToe(new Salon() { Naam = "Salon", HeeftSchouw = true });
huis.VoegKamerToe(new Gang() { Naam = "Gang", Oppervlakte = 20 });
huis.VoegKamerToe(new Badkamer() { Naam = "Badkamer" });
huis.VoegKamerToe(new Kamer() { Naam = "Slaapkamer" });
huis.ToonOverzicht();
Salon: 500 euro
Gang: 200 euro
Badkamer: 500 euro
Slaapkamer: 400 euro
Totaal: 1600 euro

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

  • De lijst bevat Kamer-variabelen, maar in de heap zitten salons, gangen en badkamers. Omdat Prijs virtual is, geeft kamer.Prijs in dezelfde lus telkens de prijs van de echte kamer.
  • Een property zonder set en zonder instantievariabele kan gewoon iets uitrekenen. Zet iemand later HeeftSchouw op true, dan klopt de prijs van het salon vanzelf.

Luchtvaartshow (Essential, GPT)

Op een luchtvaartshow doet elk toestel zijn eigen nummertje: een dubbeldekker, een straaljager, een helikopter en een luchtballon. Je werkt met overerving, polymorfisme, base, een Dictionary en static.

Potloodtekening: een dubbeldekker, een straaljager, een helikopter en een luchtballon vliegen elk op hun eigen manier, de robot kondigt aan met een megafoon.

De basisklasse Vliegtoestel

  • Een property ModelNaam met een private set, en een constructor die de modelnaam krijgt.
  • Een virtual methode Vlieg() die Het vliegtoestel [ModelNaam] stijgt op. toont.
  • Een private static Dictionary<string, Vliegtoestel> als register: elk nieuw toestel zet zichzelf erin vanuit de constructor, met Add en de modelnaam als sleutel. Een modelnaam mag dus maar één keer voorkomen: een tweede keer dezelfde naam geeft een ArgumentException.
  • Een static methode ToonRegister() die van elk toestel in het register de modelnaam en het type toont.
  • Een static methode Show() die elk toestel uit het register laat vliegen.

De childs

  • Straaljager: Vlieg() toont enkel De straaljager [ModelNaam] doorbreekt de geluidsmuur!
  • Helikopter: heeft een extra property RotorSnelheid, die je via de constructor meegeeft. Vlieg() roept eerst base.Vlieg() op en toont daarna De helikopter [ModelNaam] draait met een rotorsnelheid van [RotorSnelheid] toeren per minuut.
  • Luchtballon: Vlieg() toont enkel De luchtballon [ModelNaam] zweeft traag met de wind mee.

De dubbeldekker heeft geen eigen klasse: dat is een gewoon Vliegtoestel.

Testcode. Je code werkt als je exact deze uitvoer krijgt.

Vliegtoestel dubbeldekker = new Vliegtoestel("Tiger Moth");
Straaljager straaljager = new Straaljager("F-16");
Helikopter helikopter = new Helikopter("Agusta A109", 380);
Luchtballon ballon = new Luchtballon("Zonnebloem");

Vliegtoestel.ToonRegister();
Vliegtoestel.Show();

try
{
    Straaljager tweede = new Straaljager("F-16");
}
catch (ArgumentException)
{
    Console.WriteLine("Die modelnaam staat al in het register.");
}
Geregistreerde toestellen:
Model: Tiger Moth, Type: Vliegtoestel
Model: F-16, Type: Straaljager
Model: Agusta A109, Type: Helikopter
Model: Zonnebloem, Type: Luchtballon
De show begint!
Het vliegtoestel Tiger Moth stijgt op.
De straaljager F-16 doorbreekt de geluidsmuur!
Het vliegtoestel Agusta A109 stijgt op.
De helikopter Agusta A109 draait met een rotorsnelheid van 380 toeren per minuut.
De luchtballon Zonnebloem zweeft traag met de wind mee.
Die modelnaam staat al in het register.

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

  • Omdat de childs met base(modelNaam) de constructor van Vliegtoestel oproepen, registreren ze zichzelf vanzelf. Het register staat één keer in de parent.
  • Show kent enkel Vliegtoestel, en toch vliegt elk toestel op zijn manier. Komt er morgen een Zweefvliegtuig bij, dan verandert er aan Show niets.

Dierentuin advanced (Essential)

Werk verder aan je Dierentuin uit hoofdstuk 14, met Koe, Slang en Varken. Het menu krijgt er twee keuzes bij.

Deel 1. Keuze 5. Praten per soort: de gebruiker typt een soort (koe, slang of varken), en enkel de dieren van die soort zeggen iets. Staat er geen enkel dier van die soort in de lijst, of typt de gebruiker iets anders, dan toont het programma Geen dieren van die soort.

Schrijf daarvoor een methode static bool IsVanSoort(Dier dier, string soort) die met een switch en is zegt of het dier van die soort is. Zo heb je maar één lus nodig.

Deel 2. Keuze 6. Gifcontrole: Slang krijgt een bool-property Giftig. Maak in VulStartDieren de slang van 3 kg giftig, en zet er een tweede slang van 5 kg bij die niet giftig is. Keuze 6 toont elke giftige slang met haar gewicht, en daarna het aantal giftige slangen.

Een voorbeeld:

1. Dier verwijderen
2. Gemiddeld gewicht
3. Dieren praten
4. Opnieuw beginnen
5. Praten per soort
6. Gifcontrole
q. Stoppen
>5
Welke soort (koe, slang of varken)?
>slang
Sssssss
Sssssss
...
>5
Welke soort (koe, slang of varken)?
>giraf
Geen dieren van die soort.
...
>6
Giftige slang van 3 kg
Aantal giftige slangen: 1

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

  • In deel 1 is is een ja-of-neevraag. Omzetten hoeft niet: Zegt staat al in Dier, en late binding kiest het juiste geluid.
  • In deel 2 kan het niet zonder omzetten: Giftig bestaat enkel in Slang. dier is Slang slang test en zet om in één keer, en dankzij && wordt slang.Giftig enkel bekeken als het echt een slang is.
  • Zie Het is keyword en Pattern matching: is met een variabele.

Stevens dierenshow (Essential)

Stagiair Steven werkt verder aan de dierentuin van Dierentuin advanced. Voor de dierenshow moet elk dier één keer op het podium komen en praten, en daarna moet elke koe gemolken worden. Daarvoor gaf hij Koe een extra methode GeefMelk. De klassen zien er zo uit (in je project staat elke klasse in een eigen bestand; Giftig laat hij voor de show weg):

internal abstract class Dier
{
    public int Gewicht { get; set; }

    public abstract void Zegt();
}

internal class Koe : Dier
{
    public override void Zegt()
    {
        Console.WriteLine("Moeeeh");
    }

    public void GeefMelk()
    {
        Console.WriteLine("De koe geeft een emmer melk.");
    }
}

internal class Slang : Dier
{
    public override void Zegt()
    {
        Console.WriteLine("Sssssss");
    }
}

internal class Varken : Dier
{
    public override void Zegt()
    {
        Console.WriteLine("Knor knor");
    }
}

Voor de show zelf vroeg Steven een A.I. om “een lus die bij elk soort dier het juiste doet”. Hij plakte dit in Main:

List<Dier> dieren = new List<Dier>();
dieren.Add(new Koe() { Gewicht = 600 });
dieren.Add(new Slang() { Gewicht = 3 });
dieren.Add(new Varken() { Gewicht = 110 });
dieren.Add(new Koe() { Gewicht = 550 });

foreach (Dier dier in dieren)
{
    if (dier is Dier)
        Console.WriteLine($"Op het podium: een dier van {dier.Gewicht} kg");
    else if (dier is Koe)
        ((Koe)dier).GeefMelk();

    switch (dier.GetType().Name)
    {
        case "Koe":
            ((Koe)dier).Zegt();
            break;
        case "Slang":
            ((Slang)dier).Zegt();
            break;
    }
}

Deel 1. Het compileert zonder één waarschuwing. Voer het uit. Er wordt geen enkele koe gemolken. Waarom niet?

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

Deel 2. Ook het varken zegt niets. Waarom niet? En waarom had Steven die hele switch niet nodig?

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

Deel 3. Herschrijf de lus. Elk dier komt op het podium en praat, en elke koe geeft daarna melk. Gebruik geen cast met haakjes.

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

  • is Dier op een element van een List<Dier> is altijd true, tenzij het element null is. Test je in een if met else if op meerdere types, zet dan het meest specifieke type eerst.
  • Heeft elk object de methode al, omdat ze virtual of abstract is in de parent, dan heb je is niet nodig. is is voor wat enkel een child kan, zoals GeefMelk.
  • Zie Het is keyword, Pattern matching: is met een variabele en de stagiair in Polymorfisme in de praktijk.

Stevens Pokédex (Essential)

Stagiair Steven houdt een pokedex bij: een lijst waarin elke Pokémon maar één keer mag staan. Om het kort te houden werkt hij met een ingekorte Pokemon-klasse, met een naam, een level en twee base-stats. Voor het vergelijken vroeg hij een A.I. om een Equals, en die zette hij in de klasse:

internal class Pokemon
{
    public string Naam { get; private set; }
    public int Level { get; private set; }
    public int HP_Base { get; private set; }
    public int Attack_Base { get; private set; }

    public Pokemon(string naam, int level, int hpBase, int attackBase)
    {
        Naam = naam;
        Level = level;
        HP_Base = hpBase;
        Attack_Base = attackBase;
    }

    public bool Equals(Pokemon andere)
    {
        return Naam == andere.Naam && Level == andere.Level
            && HP_Base == andere.HP_Base && Attack_Base == andere.Attack_Base;
    }
}

Om te testen schreef hij in Main:

Pokemon a = new Pokemon("Pikachu", 5, 35, 55);
Pokemon b = new Pokemon("Pikachu", 5, 35, 55);
Console.WriteLine($"a.Equals(b): {a.Equals(b)}");

List<Pokemon> pokedex = new List<Pokemon>();
pokedex.Add(a);
if (!pokedex.Contains(b))
{
    pokedex.Add(b);
}
Console.WriteLine($"Aantal in de pokedex: {pokedex.Count}");

Pokemon[] team = new Pokemon[6];
team[0] = new Pokemon("Bulbasaur", 5, 45, 49);
team[1] = a;

bool zitInTeam = false;
for (int i = 0; i < team.Length; i++)
{
    if (b.Equals(team[i]))
    {
        zitInTeam = true;
    }
}
Console.WriteLine($"Pikachu zit in het team: {zitInTeam}");

“De eerste regel toont True, dus mijn Equals werkt”, zegt hij.

Deel 1. Het compileert zonder één waarschuwing. Voer het uit. Waarom staan er twee Pikachu’s in de pokedex, terwijl a.Equals(b) True geeft? Dat het programma daarna crasht, is voor deel 2.

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

Deel 2. Na die twee regels crasht het programma. Welke regel is de schuldige, en waarom?

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

Deel 3. Herschrijf de Equals van Steven zodat de pokedex en het team kloppen, en vergeet GetHashCode niet. Welke uitvoer verwacht je nu?

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

Deel 4. Nu je eigen Pokémon. Overschrijf Equals en GetHashCode in je Pokemon-klasse uit hoofdstuk 10, op dezelfde manier. Twee Pokémon zijn gelijk als hun naam, hun level en hun zes base-stats gelijk zijn. De _Full-stats vergelijk je niet: die worden uitgerekend uit de base-stats en het level.

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

Ganzenbord Dams Van Camp editie

Bordspelen zoals Ganzenbord of Monopoly zijn goede oefeningen voor polymorfisme. Het speelbord is een lijst van objecten, met elk vakje een object in die lijst. Sommige vakjes kunnen meer dan andere, dus de lijst bevat objecten van verschillende child-klassen met één basisklasse. Deze opgave komt uit het examen van de tweede zit in augustus 2021. Het grootste deel is herhaling van hoofdstuk 9 tot en met 12; het polymorfisme zit in het laatste stuk.

De kinderen van meneer Dams en Van Camp spelen graag Ganzenbord. Laatst vroegen ze zich af of mijn studenten eigenlijk ganzenbord zouden kunnen programmeren. “Uiteraard!” antwoordde meneer Dams. “Een vereenvoudigde versie, maar toch.” En hier zit je nu dus, met de basis van ganzenbord voor één speler. Geen flauw benul hoe ganzenbord werkt? Geen probleem, alles staat in de opgave.

Het spel. Eén speler probeert zo ver mogelijk te geraken op een rij van 10 vakjes. Sommige vakjes doen iets met de speler die erop belandt (bijvoorbeeld een stapje vooruit), andere doen niets. Het bord wordt bij de start van elk spel willekeurig aangemaakt. De speler rolt een dobbelsteen om te weten hoeveel vakjes zijn pion vooruit gaat, en verdient punten naarmate hij verder geraakt.

De klasse Dobbelsteen heeft een static methode Rol die een willekeurig getal teruggeeft: 1, 2 of 3.

De klasse Speelvakje heeft:

  • Een int-property BeweegVakjes met een private set: zoveel vakjes gaat de speler vooruit (positief) of achteruit (negatief) als hij op dit vakje belandt.
  • Een default constructor die BeweegVakjes zo instelt:
    • 30% kans op +1 of +2
    • 20% kans op -1 of -2
    • 50% kans op 0
  • Een virtual methode ToonVakje die op de huidige plaats van de cursor de waarde van BeweegVakjes schrijft. Is de waarde positief of 0, dan met een + ervoor, zoals +2 of +0. Een negatieve waarde toon je met de -, zoals -2.

De klasse Ganzenbord heeft:

  • Een private lijst speelvakjes met 10 Speelvakje-objecten, die de default constructor aanmaakt.
  • Een private instantievariabele pionIndex: de index van het vakje waar de speler nu staat.
  • Een autoproperty HuidigeScore die standaard 0 is.
  • Een methode BeweegPion die een int krijgt en een bool teruggeeft.
  • Een methode TekenBord die het bord op het scherm toont.

BeweegPion krijgt het getal dat de speler gerold heeft. pionIndex gaat zoveel vooruit, en dan:

  • Is pionIndex 10 of meer, dan is de speler voorbij het laatste vakje en heeft hij gewonnen. HuidigeScore gaat 10 omhoog en de methode geeft true terug.
  • Is pionIndex 9 of lager, dan kijk je in de lijst op welk vakje de speler beland is. De BeweegVakjes van dat vakje tel je op bij pionIndex. Op het nieuwe vakje gebeurt daarna niets meer, ook al zou je daar vooruit of achteruit moeten.
    • Komt pionIndex zo onder 0, dan gaat HuidigeScore 10 omlaag, komt pionIndex terug op 0 en geeft de methode false terug.
    • Ligt pionIndex tussen 0 en 9, dan geeft de methode false terug.
    • Is pionIndex 10 of meer, dan heeft de speler toch gewonnen: HuidigeScore gaat 10 omhoog en de methode geeft true terug.

TekenBord toont de 10 vakjes naast elkaar: het overloopt de lijst en roept van elk vakje ToonVakje op. Daarna overschrijft het op de plaats van de pion het cijfer met een T. Staat de speler op index 3, het vierde vakje, dan kan dat zo uitzien:

-1+2+1-T+0+0-2+1+1+0

Het eerste vakje heeft waarde -1, het tweede +2, het derde +1. Van het vierde zie je enkel het minteken, want op het cijfer staat de T. Daarna volgen +0 en +0, enzovoort.

De spellus in Main

  • Maak een Ganzenbord-object.
  • Herhaal tot de speler aan het einde is:
    • Toon het bord met TekenBord.
    • Rol met Rol, en toon onder het bord bijvoorbeeld Je rolde 2.
    • Geef de worp aan BeweegPion. Het resultaat bepaalt of de lus doorgaat.
    • Wacht tot de gebruiker op enter duwt, en maak dan het scherm leeg.
  • Toon op het einde de HuidigeScore.

Polymorfisme. Werkt alles, maak dan een klasse KleurVakje die erft van Speelvakje. Zo’n vakje doet alles wat een gewoon vakje doet, maar ToonVakje toont de waarde in kleur: witte letters op een rode achtergrond als BeweegVakjes negatief is, anders zwarte letters op een groene achtergrond.

Geef Ganzenbord een tweede constructor die een bool krijgt: true vult de lijst met KleurVakjes, false met gewone Speelvakjes. Bij de start vraagt het programma of de speler de kleurenversie of de zwart-witversie wil spelen.

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

  • TekenBord weet niet of een vakje gekleurd is. Het roept ToonVakje op, en het echte object beslist hoe het eruitziet.
  • KleurVakje herhaalt de tekst niet: het zet de kleur, laat base.ToonVakje() het werk doen en zet de kleur terug.
  • De tweede constructor bepaalt welke soort vakjes in de lijst komt. De rest van Ganzenbord blijft hetzelfde.

Grand Theft Auto: San Andreas - Polymorfisme (Final Essentials, GPT)

Los Santos heeft een divers wagenpark. Het is jouw taak om een universeel bestuurssysteem te maken dat werkt voor elk voertuig, van sportwagens tot politiemotoren.

Stap 1: de abstracte klasse. Maak een abstracte klasse Voertuig met:

  • een property Merk (string);
  • een property Snelheid (int), standaard 0, die enkel Voertuig en zijn childs kunnen aanpassen;
  • een abstracte methode Beweeg(), die de snelheid instelt en de beweging toont;
  • een virtuele methode ToonInfo(), die Dit is een voertuig van merk [Merk]. toont.

Stap 2: de garage. Maak drie klassen die erven van Voertuig:

  • Sportwagen
    • Beweeg(): zet Snelheid op 150 en toont ZOEF! De [Merk] scheurt over de snelweg met 150 km/u.
    • ToonInfo(): toont Een glimmende sportwagen van [Merk]. Pas op voor krassen!
  • Pantserwagen
    • Beweeg(): zet Snelheid op 60 en toont BROEM... De [Merk] rijdt traag maar stopt voor niets of niemand.
    • ToonInfo(): toont Een gepantserd voertuig van [Merk]. Kogelvrij!
  • PolitieMotor
    • Heeft een bool-property SireneAan, standaard false.
    • Beweeg(): staat de sirene aan, dan zet hij Snelheid op 120 en toont hij WIEUWIEUWIEU! De [Merk] achtervolgt de verdachte met 120 km/u! Anders zet hij Snelheid op 50 en toont hij De [Merk] patrouilleert rustig met 50 km/u.
    • ToonInfo() overschrijft hij niet.

Stap 3: het verkeer. Maak in Main een List<Voertuig> met deze voertuigen, in deze volgorde: een sportwagen Infernus, een politiemotor HPV-1000, een pantserwagen Rhino en een sportwagen Cheetah. Laat elk voertuig in één lus bewegen, en toon daarna in een tweede lus van elk voertuig de info.

Stap 4: de bankoverval. Toon Er is een bankoverval gemeld!. Zet bij elke politiemotor in de lijst de sirene aan, en laat daarna alle voertuigen opnieuw bewegen.

De uitvoer:

ZOEF! De Infernus scheurt over de snelweg met 150 km/u.
De HPV-1000 patrouilleert rustig met 50 km/u.
BROEM... De Rhino rijdt traag maar stopt voor niets of niemand.
ZOEF! De Cheetah scheurt over de snelweg met 150 km/u.
Een glimmende sportwagen van Infernus. Pas op voor krassen!
Dit is een voertuig van merk HPV-1000.
Een gepantserd voertuig van Rhino. Kogelvrij!
Een glimmende sportwagen van Cheetah. Pas op voor krassen!
Er is een bankoverval gemeld!
ZOEF! De Infernus scheurt over de snelweg met 150 km/u.
WIEUWIEUWIEU! De HPV-1000 achtervolgt de verdachte met 120 km/u!
BROEM... De Rhino rijdt traag maar stopt voor niets of niemand.
ZOEF! De Cheetah scheurt over de snelweg met 150 km/u.

Deze oplossing is afgeschermd.
Studenten krijgen de sleutel van hun lesgever. Heb je de sleutel al eens op dit toestel gebruikt, dan verschijnt de oplossing hier vanzelf.

  • Beweeg en ToonInfo staan in Voertuig, dus de lussen roepen ze op zonder te vragen wat voor voertuig het is. De PolitieMotor zonder eigen ToonInfo krijgt de tekst van Voertuig.
  • SireneAan bestaat enkel in PolitieMotor. Daar heb je is PolitieMotor motor voor nodig.
  • Beweeg is abstract: een nieuw soort voertuig kan niet vergeten te bewegen.