Overerving, interfaces en polymorfisme in Java
extends en super, overschrijf overgeërfde methoden en geef je klassen bruikbare toString-, equals- en hashCode-methoden uit Object. Definieer gemeenschappelijk gedrag met abstracte klassen en interfaces, voorspel met polymorfisme en dynamische binding welke methode wordt uitgevoerd en modelleer vaste keuzemogelijkheden en eenvoudige gegevens met enums en records. Uitgewerkte codevoorbeelden laten elke stap zien, samen met de fouten die beginners het vaakst maken.BeginnerLes 8 van 11 in Cursus Java programmeren: klassen, overerving, collecties en streams
Wat je leert
- Bouw met
extendseen nieuwe klasse op een bestaande klasse, weet wat de nieuwe klasse overerft en zorg dat het overgeërfde deel van elk object correct wordt geïnitialiseerd metsuper.- Maak een subklasse met extendsDeclareer een subklasse, bepaal welke leden die overerft en kan gebruiken en beslis met de is-een-test of overerving past.
- Initialiseer objecten met superklasseconstructorenGeef waarden door aan de superklasseconstructor met
super(...), voorspel in welke volgorde constructoren worden uitgevoerd en vermijd de klassieke fouten met constructoren.
- Maak een subklasse met extends
- Verander overgeërfd gedrag door methoden te overschrijven, bouw met
supervoort op de versie van de superklasse en geef je klassen betekenisvolletoString-,equals- enhashCode-methoden uitObject.- Overschrijf overgeërfde methodenVervang of breid een overgeërfde methode uit met een correcte overschrijving, roep de versie van de superklasse aan met
superen onderscheid overschrijven van overladen. - Gebruik en overschrijf de methoden van ObjectLeg uit waarom elk object
toString,equals,hashCodeengetClassheeft, lees hun standaardgedrag af en schrijf een bruikbaretoString. - Overschrijf equals en hashCode correctDefinieer met een correcte
equals(Object)wanneer twee objecten gelijk zijn, houdhashCodedaarmee consistent en behandel gelijkheid binnen een klassenhiërarchie.
- Overschrijf overgeërfde methoden
- Gebruik abstracte klassen om code te delen tussen nauw verwante klassen, gebruik interfaces om gedrag vast te leggen voor niet-verwante klassen en kies het juiste middel voor een ontwerp.
- Schrijf abstracte klassen en abstracte methodenDeclareer abstracte klassen en methoden, vul die aan in concrete subklassen en gebruik een abstracte klasse om velden en code te delen.
- Definieer en implementeer interfacesSchrijf een interface als contract, implementeer die in één of meer klassen en combineer interfaces met overerving.
- Voeg default-methoden, statische methoden en privémethoden toe aan interfacesGeef interfaces gemeenschappelijk gedrag met default-methoden, voeg hulpmethoden toe met statische methoden en privémethoden en los conflicten tussen default-methoden op.
- Kies tussen een abstracte klasse en een interfaceVergelijk abstracte klassen en interfaces en kies voor een bepaald ontwerp de juiste optie of gebruik ze allebei.
- Schrijf abstracte klassen en abstracte methoden
- Verwijs naar objecten via algemene typen, voorspel welke methode tijdens de uitvoering wordt uitgevoerd, zet typen veilig om en schrijf code die blijft werken als nieuwe subklassen worden toegevoegd.
- Verwijs naar objecten via superklassetypen en interfacetypenOnderscheid het gedeclareerde type van een variabele van het werkelijke type van het object en gebruik superklassetypen en interfacetypen voor variabelen, parameters en lijsten.
- Voorspel welke methode wordt uitgevoerd bij dynamische bindingVolg methodeaanroepen door een klassenhiërarchie en onderscheid wat de compiler bepaalt van wat tijdens de uitvoering wordt bepaald.
- Gebruik casts en instanceof om met verschillende typen te werkenGebruik veilig downcasting met
instanceofen patroonherkenning, herkenClassCastExceptionen weet wanneer een typetest wijst op een ontbrekende overschreven methode. - Ontwerp flexibele code met polymorfismeSchrijf methoden en collecties voor algemene typen, zodat nieuwe klassen zonder wijzigingen kunnen worden gebruikt, en maak klassen sorteerbaar met
Comparable.
- Verwijs naar objecten via superklassetypen en interfacetypen
- Geef een vaste verzameling keuzemogelijkheden weer met een enum en een eenvoudig gegeven met een record, en gebruik daarbij de leden die de compiler voor je genereert.
- Geef vaste verzamelingen waarden weer met enumsDeclareer enums, gebruik hun ingebouwde methoden en
switchen geef enumconstanten velden, constructoren en gedrag. - Modelleer gegevens met recordsDeclareer records voor eenvoudige gegevens, gebruik de gegenereerde constructor, accessors,
equals,hashCodeentoStringen valideer gegevens in een compacte constructor.
- Geef vaste verzamelingen waarden weer met enums
Overerving in Java: uitleg
1Breid klassen uit met overerving
Een subklasse begint met de velden en het gedrag van een bestaande klasse en voegt daarna toe wat haar anders maakt. Die relatie beïnvloedt zowel hoe andere code het object kan gebruiken als hoe Java het opbouwt. Volg een persoon en een student door hun declaraties en constructoren: private gegevens blijven onder controle van de superklasse en constructoraanroepen leveren de waarden die de overgeërfde toestand nodig heeft.
1.1 Bouw een subklasse en bescherm haar gegevens
Overerving maakt een subklasse op basis van een bestaande superklasse. In class Student extends Person is elke student een persoon. Een studentobject bevat zijn persoonstoestand en zijn extra studenttoestand samen in één object.
Voeg de studentspecifieke onderdelen toe
Hier zie je een kleine superklasse en een subklasse:
class Person {
private String name;
public Person(String name) { this.name = name; }
public String getName() { return name; }
}
class Student extends Person {
private int credits;
public Student(String name) { super(name); }
public void study() { credits += 5; }
public int getCredits() { return credits; }
}
Student overerft getName(). De klasse voegt een veld voor studiepunten en twee methoden toe. Na Student s = new Student("Ada Lovelace"); begint het veld voor studiepunten op 0. Twee aanroepen van s.study() verhogen het eerst tot 5 en dan tot 10. s.getName() geeft nog steeds “Ada Lovelace” terug: de overgeërfde methode leest het private veld dat in Person is gedeclareerd.
De instructie super(name) geeft de naam door aan de constructor van Person. Constructoren worden niet overgeërfd, dus Student declareert een eigen constructor. Een subklasse mag methoden toevoegen en overgeërfd gedrag overschrijven, maar kan een overgeërfde public methode niet uit haar publieke interface verwijderen.
Bepaal welke leden toegankelijk zijn
| Toegangsmodifier | Waar het lid toegankelijk is |
|---|---|
public | Elke klasse met toegang tot het type dat het lid declareert |
protected | Hetzelfde pakket, plus toegang voor subklassen in andere pakketten |
| Geen modifier | Hetzelfde pakket |
private | De klasse die het lid declareert |
Private leden worden niet overgeërfd. Het private veld name bestaat nog steeds in elke student, maar Student kan het niet rechtstreeks lezen. Door het veld private te houden en een getter aan te bieden, kan Person zijn eigen gegevens beheren. Voor protected toegang tot instanties over pakketgrenzen heen geldt een extra beperking: code in een subklasse benadert het lid via zichzelf of een referentie van het eigen subklassetype, niet via een willekeurige superklassereferentie.
Test de relatie voordat je uitbreidt
Java staat één directe superklasse per klasse toe. Een keten zoals GraduateStudent extends Student en Student extends Person is geldig; extends Student, Teacher is dat niet. Een klasse zonder expliciete superklasse breidt Object uit. Object zelf heeft geen superklasse.
Gebruik de is-een-test: kan elk subklasseobject worden gebruikt waar de superklasse wordt verwacht? Een vrachtwagen is een voertuig. Een klant heeft een adres, maar een klant is geen adres. Gebruik voor die tweede relatie compositie:
class Customer {
private Address address;
}
Hier is Address een andere klasse. Via het veld kan een klant een adres gebruiken zonder te beweren er zelf één te zijn. Als je een klasse alleen uitbreidt om een handige methode te gebruiken, erf je ook het hele publieke contract ervan. Tot slot kan een final-klasse niet worden uitgebreid. Omdat String final is, compileert class ShortString extends String niet.
Belangrijkste punten
- Een subklasseobject bevat de toestand van de superklasse en alle toestand die de subklasse toevoegt.
- Private superklassevelden worden benaderd via de methoden van de superklasse.
- Een klasse breidt één directe superklasse uit; constructoren worden niet overgeërfd.
- Gebruik overerving voor een is-een-relatie en compositie voor een heeft-een-relatie.
- Een
final-klasse kan niet worden uitgebreid.
1.2 Geef waarden door via een constructorketen
Een subklasseconstructor moet de constructie van het superklassedeel van hetzelfde object regelen. Hij kan een superklasseconstructor aanroepen met super(...), of de taak doorgeven aan een andere constructor in de eigen klasse met this(...).
Kies de superklasseconstructor
class Person {
private final String name;
Person(String name) { this.name = name; }
}
class Student extends Person {
private final String studentId;
Student(String name, String studentId) {
super(name);
this.studentId = studentId;
}
Student(String name) {
this(name, "no id yet");
}
}
De constructor met twee argumenten geeft de naam door aan Person en slaat zelf de identificatie op. De constructor met één argument geeft de taak aan die constructor door. Een constructor bevat hoogstens één expliciete constructoraanroep: this(...) of super(...). Als je de this(...)-keten volgt, kom je uiteindelijk bij een constructor die super(...) aanroept.
Als geen van beide is geschreven, voegt Java super() in. Dat mislukt hier omdat Person alleen een constructor heeft die een naam vereist. Zodra je een constructor declareert, levert Java geen standaardconstructor zonder parameters. Een klasse zonder gedeclareerde constructor krijgt een standaardconstructor die super() aanroept.
new Person(name) schrijven binnen Student doet iets anders: het construeert een afzonderlijk object. Het initialiseert niet de persoonstoestand van de huidige student en vervangt ook niet de vereiste aanroep van de superklasseconstructor.
Volg de initialisatie in volgorde
class Base {
Base() { System.out.println("Base ready"); }
}
class Middle extends Base {
Middle() { System.out.println("Middle ready"); }
}
class Top extends Middle {
Top() { super(); System.out.println("Top ready"); }
}
new Top() doorloopt de constructorketen tot aan Object. Als de aanroepen terugkeren, is de uitvoer “Base ready”, “Middle ready” en dan “Top ready”. De veldinitialisatoren van elke klasse worden uitgevoerd nadat de superklasseconstructor is teruggekeerd en vóór de rest van de constructorbody van die klasse.
Een overschrijfbare methode die door een superklasseconstructor wordt aangeroepen, kan al een overschrijving in een subklasse uitvoeren. Als die overschrijvende methode een referentieveld van de subklasse leest waarvan de toewijzing nog niet is uitgevoerd, ziet ze null, zelfs als het veld een initialisator bij de declaratie heeft. Gebruik constructoren om de toestand te initialiseren; vermijd aanroepen van methoden die subklassen kunnen overschrijven.
Herken de wijziging in Java 25
In Java 25 kan een constructor parameters valideren vóór zijn expliciete constructoraanroep:
Student(String name, String studentId, int credits) {
if (credits < 0) {
throw new IllegalArgumentException("negative credits");
}
super(name);
this.studentId = studentId;
}
Java 25 staat ook eenvoudige vroege toewijzingen toe aan velden die in de huidige klasse zonder initialisator bij de declaratie zijn gedeclareerd. Het blijft verboden om vóór de aanroep de velden van het huidige object te lezen, de instantiemethoden ervan aan te roepen of this door te geven. Houd bij standaardtaalversies van vóór Java 25 super(...) of this(...) als eerste. Java 22–24 bood eerdere vormen als voorlopige functies.
Belangrijkste punten
super(...)construeert het superklassedeel van het huidige object.- Een impliciete
super()vereist een toegankelijke superklasseconstructor zonder parameters. this(...)geeft de taak door binnen dezelfde klasse; de keten moet een superklasseconstructor bereiken.- Veldinitialisatoren worden uitgevoerd nadat de superklasseconstructor is teruggekeerd.
- Java 25 staat beperkt werk toe vóór een expliciete constructoraanroep.
- Een overschreven methode die tijdens de constructie wordt aangeroepen, kan subklassevelden lezen voordat daaraan waarden zijn toegewezen.
2Overschrijf methoden en gebruik de klasse Object
Overgeërfde methoden geven verwante objecten een gemeenschappelijk uitgangspunt. Door methoden te overschrijven kan elke subklasse op haar eigen manier op dezelfde aanroep reageren, terwijl de methoden die van Object zijn overgeërfd bepalen hoe een object wordt afgedrukt en met andere objecten wordt vergeleken. Kleine verschillen in signaturen zijn hier van belang: een methode kan er voor een lezer goed uitzien, maar toch niet de methode vervangen die bibliotheekcode daadwerkelijk aanroept. Ook gelijkheid kent beloften waarop collecties op basis van hashcodes vertrouwen.
2.1 Vervang overgeërfd gedrag met een overschrijvende methode
Een overschrijvende methode geeft een subklasse een eigen implementatie van een overgeërfde instantiemethode. De naam en parametertypen komen overeen met die van de oorspronkelijke methode, zodat aanroepers dezelfde operatie kunnen blijven gebruiken.
Breid een werkende methode uit
Stel dat de jaarbeloning van een werknemer uit twaalf maandbetalingen bestaat. Een manager ontvangt daarnaast een jaarlijkse bonus:
class Employee {
private final double monthlySalary;
Employee(double monthlySalary) {
this.monthlySalary = monthlySalary;
}
public double yearlyPay() { return 12 * monthlySalary; }
}
class Manager extends Employee {
private final double bonus;
Manager(double monthlySalary, double bonus) {
super(monthlySalary);
this.bonus = bonus;
}
@Override
public double yearlyPay() {
return super.yearlyPay() + bonus;
}
}
Voor new Manager(3000.0, 5000.0) geeft de aanroep van de superklassemethode 36.000,0 terug. Als je daar 5.000,0 bij optelt, krijg je 41.000,0. super.yearlyPay() kiest bewust de implementatie van de superklasse. De superklassemethode blijft beschikbaar binnen de subklasse, ook al voeren gewone aanroepen op de manager de overschrijvende methode uit.
Als je return yearlyPay() + bonus; schrijft, roep je de eigen methode van de manager opnieuw aan. Geen enkele aanroep wordt voltooid, zodat de uitvoering uiteindelijk mislukt met StackOverflowError. De ontbrekende super. verandert de betekenis van de aanroep.
Laat de compiler de signatuur controleren
Zet @Override boven elke overschrijvende methode. Een spelfout zoals yearlypay() of een extra parameter veroorzaakt dan een compilatiefout. Zonder de annotatie kan Java een nieuwe methode accepteren die aanroepers nooit gebruiken.
Een geldige overschrijving volgt deze regels:
- De parametertypen komen precies overeen.
- Het retourtype is hetzelfde, of een subtype bij een objectretourtype. Die toegestane versmalling heet een covariant retourtype.
- De toegang is minstens even open als die van de overgeërfde methode.
- De methode voegt geen gecontroleerde excepties toe die de overgeërfde declaratie verbiedt.
Zo kan public String toString() niet worden vervangen door String toString() met pakkettoegang. Bestaande aanroepers moeten toegang houden.
Onderscheid overschrijven van overladen en verbergen
| Declaratie | Wat die doet |
|---|---|
| Dezelfde instantiemethodesignatuur in een subklasse | Overschrijft overgeërfd gedrag |
| Dezelfde naam, andere parameterlijst | Voegt een overladen methodevariant toe |
| Dezelfde statische methodesignatuur in een subklasse | Verbergt een statische methode |
role() en role(String language) zijn overladen methodevarianten; de tweede vervangt de eerste niet. De keuze tussen overladen methoden gebruikt tijdens de compilatie de gedeclareerde argumenttypen. Statische methoden worden ook gekozen op basis van het type dat tijdens de compilatie wordt gebruikt, dus worden ze niet dynamisch overschreven.
Een private methode wordt niet overgeërfd; een methode met dezelfde naam in de subklasse staat daar los van. Een final-methode kan niet worden overschreven en constructoren kunnen niet worden overschreven. Naast compileren moet de methode nog steeds haar oorspronkelijke doel vervullen. Een overschrijvende methode van een manager die de maandbeloning teruggeeft, zou code die jaarbeloningen optelt verstoren, ook als de signatuur correct was.
Belangrijkste punten
- Een overschrijvende methode komt overeen met de naam en parametertypen van een overgeërfde instantiemethode.
@Overridespoort onbedoeld overladen methoden en spelfouten op.super.method()hergebruikt de implementatie van de superklasse.- Een overschrijvende methode mag de toegang niet beperken of de toegestane gecontroleerde excepties uitbreiden.
- Bij overladen veranderen de parameters; statische methoden worden verborgen en final-methoden kunnen niet worden overschreven.
2.2 Geef objecten bruikbare tekst en bepaal hun klasse
Elke Java-klasse breidt Object direct of indirect uit. Arrays zijn ook objecten. Deze gemeenschappelijke basis biedt methoden zoals toString(), equals(Object), hashCode() en getClass(), ook als een klasse er zelf geen declareert.
Vervang de standaardbeschrijving bij het afdrukken
De overgeërfde Object.toString() levert een klassenaam, @ en de hashcode van het object in hexadecimale notatie. Tekst zoals “school.Person@1b6d3586” geeft de klasse aan, maar zegt niets over de gegevens van de persoon. Het getal kan per uitvoering veranderen en is geen geheugenadres dat door Java wordt gegarandeerd.
Een bruikbare overschrijvende methode toont de velden die lezers nodig hebben. Hier heeft een studentobject getName() overgeërfd en bevat het een veld credits:
@Override
public String toString() {
return getName() + " (" + credits + " credits)";
}
Voor een student met de naam Ada Lovelace en 10 studiepunten drukt System.out.println(s) “Ada Lovelace (10 credits)” af. De afdrukaanroep haalt automatisch de tekst van het object op. Stringconcatenatie doet dit ook: "Student: " + s bevat dezelfde beschrijving.
Voor een null-referentie leveren afdrukken en concatenatie “null” op. Een expliciete aanroep s.toString() wanneer s null is, werpt NullPointerException op, omdat er geen object is dat die aanroep kan ontvangen.
Als Person.toString() al de naam van de persoon teruggeeft, kan een subklasse daarop voortbouwen:
@Override
public String toString() {
return super.toString() + ", student id " + studentId;
}
Voor identificatie “S-17” levert dat “Ada Lovelace, student id S-17” op. Hoe bruikbaar super.toString() is, hangt af van wat de superklasse werkelijk teruggeeft. Als die nog de versie van Object gebruikt, draagt die de standaardtekst met klasse en hashcode bij.
Houd identiteit en klasse-informatie gescheiden
Object.equals(Object) vergelijkt identiteit: de methode geeft waar terug als beide referenties naar hetzelfde object verwijzen. Twee afzonderlijke new Person("Ada")-objecten zijn volgens die overgeërfde implementatie niet gelijk, ook al komen hun namen overeen. Klassen zoals String, Integer en ArrayList bieden overschrijvende methoden op basis van waarde.
getClass() geeft de werkelijke klasse tijdens de uitvoering:
Person p = new Student("Ada Lovelace");
System.out.println(p.getClass().getSimpleName());
Dit drukt “Student” af als Student de getoonde constructor heeft. Het gedeclareerde type van de variabele verandert de klasse van het object niet. getClass() is final en kan niet worden overschreven.
hashCode() ondersteunt collecties op basis van hashcodes. Andere methoden van Object zijn de protected methode clone() en de methoden wait(), notify() en notifyAll() voor coördinatie van threads.
Controleer de exacte spelling
Java maakt onderscheid tussen toString() en tostring(). Een methode toString(String prefix) is een overladen methodevariant, dus wordt die bij het afdrukken niet aangeroepen. Beide fouten worden door @Override opgevangen. Een tekstrepresentatie bepaalt ook niet de gelijkheid: twee objecten kunnen dezelfde tekst afdrukken terwijl ze bij vergelijking toch ongelijk zijn.
Belangrijkste punten
Objectbiedt gemeenschappelijke methoden voor alle Java-objecten, inclusief arrays.- Afdrukken en stringconcatenatie gebruiken
toString()en geven een null-referentie weer als “null”. Object.equalsvergelijkt identiteit totdat een klasse de methode overschrijft.getClass()geeft de werkelijke klasse terug en is final.
2.3 Houd gelijkheid en hashcodes consistent
Twee objecten kunnen dezelfde waarde vertegenwoordigen zonder hetzelfde object te zijn. Een boekencatalogus kan twee boekobjecten als gelijk behandelen als hun cataloguscode en editie overeenkomen. Definieer die regel door equals(Object) te overschrijven en houd de hashcode daarmee consistent.
Vergelijk de velden die de waarde bepalen
import java.util.Objects;
final class Book {
private final String code;
private final int edition;
Book(String code, int edition) {
this.code = code;
this.edition = edition;
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof Book b)) return false;
return edition == b.edition && Objects.equals(code, b.code);
}
@Override
public int hashCode() {
return Objects.hash(code, edition);
}
}
De identiteitscontrole accepteert hetzelfde object snel. De typecontrole wijst een niet-gerelateerd object of null af en introduceert daarna b als een Book. Voor primitieve editiewaarden wordt == gebruikt; Objects.equals vergelijkt codes veilig, zelfs als een code null is. Twee boeken met code “B-1042” en editie 2 zijn bij vergelijking gelijk. Editie 3 levert een andere waarde op.
De parameter moet Object zijn. Als je equals(Book other) schrijft, voeg je een overladen methodevariant toe; de overgeërfde equals(Object) blijft bestaan. Bibliotheekcode die die signatuur aanroept, zou nog steeds identiteit vergelijken. Door @Override mislukt de verkeerde declaratie tijdens de compilatie.
Volg de beloften van gelijkheid
Voor referenties die niet null zijn, moet gelijkheid zijn:
- Reflexief: een object is gelijk aan zichzelf.
- Symmetrisch: als je de twee objecten omwisselt, krijg je hetzelfde antwoord.
- Transitief: als het ene gelijk is aan een tweede en het tweede aan een derde, is het eerste gelijk aan het derde.
- Consistent: herhaalde vergelijkingen geven hetzelfde resultaat zolang de vergeleken gegevens onveranderd blijven.
Een object moet ook ongelijk zijn aan null. Gelijke objecten moeten gelijke hashcodes teruggeven. Ongelijke objecten mogen een hashcode delen: een hashbotsing bewijst geen gelijkheid. Leid de hashcode af uit dezelfde velden als de gelijkheidsbepaling, zoals Objects.hash(code, edition) hier doet.
Een hashcollectie gebruikt de hashcode om kandidaten te vinden voordat ze de gelijkheid controleert. Als je alleen equals overschrijft, kan een verzameling afzonderlijke gelijke boeken behouden of kan een map een gelijke sleutel mogelijk niet vinden. Als je een veld verandert dat voor de hashcode wordt gebruikt terwijl het object als sleutel is opgeslagen, kan dat object onder zijn oude hashcode opgeborgen blijven.
Bescherm symmetrie tussen subklassen
Stel dat een uitbreidbare Book elke instanceof Book accepteert, terwijl Ebook bestandsgrootte toevoegt aan de gelijkheidsbepaling en alleen e-books accepteert. Een boek kan een e-book met dezelfde code en editie accepteren, maar het e-book wijst het boek af. De vergelijking heeft haar symmetrie verloren.
Eén aanpak vereist overeenkomende werkelijke klassen, waarbij je vóór getClass() op null controleert. Een andere maakt de gelijkheidsbepaling van de superklasse final, zodat subklassen de gelijkheidsregel niet kunnen uitbreiden. Een final-waardeklasse, zoals hierboven, vermijdt gelijkheidsbepaling tussen subklassen volledig. Records genereren gelijkheidsbepaling en hashcodes uit hun componenten; enumconstanten hebben al een gelijkheidsbepaling op basis van identiteit die bij hun vaste waarden past.
Belangrijkste punten
- Overschrijf
equals(Object), niet een overladen methodevariant met de klasse als parameter. - Gelijkheid moet reflexief, symmetrisch, transitief en consistent zijn; vergelijking met null is onwaar.
- Gelijke objecten hebben gelijke hashcodes nodig; een gedeelde hashcode bewijst geen gelijkheid.
- Houd velden voor gelijkheidsbepaling stabiel terwijl een object een sleutel in een hashcollectie of een element van een verzameling is.
- Een superklasse en subklasse mogen geen gelijkheidsregels gebruiken die bij omkering van de operanden tegenstrijdige uitkomsten geven.
- Records en enums bieden geschikt ingebouwd gedrag voor gelijkheidsbepaling.
3Definieer contracten met abstracte klassen en interfaces
Sommige verwante klassen delen velden en werkende methoden; andere klassen delen alleen een functionaliteit. Abstracte klassen en interfaces drukken die verschillende relaties uit. Een tekenprogramma kan gemeenschappelijke vormgegevens in een abstracte klasse bewaren, terwijl een oplaadprogramma via een interface met niet-verwante apparaten werkt. Met beide aanpakken kan de compiler vereist gedrag controleren voordat het programma wordt uitgevoerd, en Java biedt precieze regels wanneer overgeërfde implementaties met elkaar concurreren.
3.1 Deel toestand en vereis gedrag van subklassen
Een abstracte klasse is een basisklasse waarvan je niet rechtstreeks een instantie kunt maken. Ze kan gemeenschappelijke velden en werkende methoden bevatten en tegelijk geselecteerde operaties overlaten aan subklassen om aan te vullen.
Laat een vereiste operatie abstract
abstract class Shape {
private final String name;
Shape(String name) { this.name = name; }
public abstract double area();
public String describe() {
return name + " with area " + area();
}
}
class Rectangle extends Shape {
private final double width;
private final double height;
Rectangle(double width, double height) {
super("rectangle");
this.width = width;
this.height = height;
}
@Override
public double area() { return width * height; }
}
area() heeft een signatuur en een puntkomma, zonder body. De methode legt vast wat elke concrete vorm moet aanbieden. Omdat Shape een abstracte methode bevat, moet de klasse ook abstract zijn. Rectangle levert de ontbrekende implementatie en je kunt er een instantie van maken.
Voor new Rectangle(3.0, 4.0) slaat de superklasseconstructor “rectangle” op. De subklasseconstructor slaat de afmetingen op. De oppervlakte is 12,0 vierkante meter als de afmetingen in meters zijn. Een aanroep van describe() geeft “rectangle with area 12.0” terug. De aanroep van area() in de overgeërfde methode voert de implementatie van de rechthoek uit.
Gebruik constructoren en concrete methoden op de gewone manier
Een abstracte klasse kan velden, constructoren en methoden met een body hebben, met de gebruikelijke toegangsniveaus. Haar constructor wordt uitgevoerd als onderdeel van het maken van een subklasseobject. new Shape() is verboden, ook als er een passende constructor bestaat; new Rectangle(3.0, 4.0) is geldig.
Een tweede subklasse kan dezelfde overgeërfde beschrijving gebruiken terwijl ze de oppervlakte anders berekent. Een cirkelimplementatie geeft Math.PI * radius * radius terug. Voor straal 1,5 m is de berekening π maal 2,25, ongeveer 7,07 vierkante meter. De gedeelde methode describe() heeft geen cirkelspecifieke tak nodig.
Een variabele kan nog steeds het abstracte type hebben: Shape s = new Rectangle(3.0, 4.0);. Java verbiedt het construeren van de abstracte klasse, niet het verwijzen naar een concreet object via dat type.
Maak ontbrekend werk zichtbaar als compilatiefout
Elke concrete subklasse moet implementaties van alle abstracte methoden aanbieden of overerven. Een subklasse die sommige methoden onvoltooid laat, moet ook als abstract worden gedeclareerd. Als een tijdelijke invulling van area() 0 zou teruggeven, zou een onvolledige concrete subklasse kunnen compileren en ongemerkt verkeerde resultaten geven. Door area() abstract te declareren, maak je dat ontbrekende werk zichtbaar voor de compiler.
Abstracte methoden kunnen niet private, static of final zijn: elk van die modifiers zou de overschrijving in de subklasse verhinderen die de operatie levert. Een abstracte klasse mag geen abstracte methoden bevatten; de modifier verhindert dan alleen rechtstreekse constructie. Gebruik dit soort basisklasse als haar subklassen slagen voor de is-een-test en baat hebben bij gedeelde toestand of code.
Belangrijkste punten
- Een abstracte klasse kan niet rechtstreeks worden geconstrueerd, maar haar constructor wordt uitgevoerd voor subklasseobjecten.
- Een abstracte methode heeft geen body en vereist een concrete implementatie.
- Een concrete subklasse moet elke overgeërfde abstracte methode aanvullen.
- Een abstract type kan worden gebruikt voor variabelen, parameters en lijsten.
- Abstracte klassen kunnen gemeenschappelijke velden, werkende methoden en vereiste operaties combineren.
3.2 Geef niet-verwante klassen een gemeenschappelijk contract
Een interface benoemt een functionaliteit die verschillende klassen kunnen bieden. Ze legt vast welke methoden via die functionaliteit beschikbaar zijn, zonder dat de klassen dezelfde instantievelden of superklasse hoeven te delen.
Declareer en implementeer de methoden
interface Chargeable {
int batteryPercent();
void charge(int minutes);
}
class Phone implements Chargeable {
private int battery;
Phone(int battery) { this.battery = battery; }
@Override
public int batteryPercent() { return battery; }
@Override
public void charge(int minutes) {
battery = Math.min(100, battery + 2 * minutes);
}
}
De abstracte methoden van de interface zijn impliciet public. De implementerende methoden moeten expliciet public zijn; als je die modifier weglaat, wordt de toegang beperkt en mislukt de compilatie. Een concrete klasse moet geschikte public implementaties van elke abstracte methode aanbieden of overerven. Een abstracte implementerende klasse mag sommige methoden overlaten aan haar subklassen.
Voor een telefoon op 35 procent voegt charge(20) 40 procentpunten toe en komt de lading uit op 75 procent. Nog eens 20 minuten zou vóór de begrenzing 115 opleveren, dus slaat Math.min 100 op. Een ElectricScooter kan dezelfde interface implementeren en daarbij slechts één punt per minuut toevoegen: vanaf 50 procent bereikt de scooter na 20 minuten 70. De interface legt de beschikbare operatie vast, terwijl de klasse het gedrag ervan levert.
Gebruik de interface als type
Chargeable device = new Phone(35);
device.charge(20);
System.out.println(device.batteryPercent());
Dit drukt 75 af. Een aanroeper kan de methoden van de interface en de public methoden gebruiken die op elk object beschikbaar zijn. Operaties die alleen bij de telefoon horen, zijn niet beschikbaar via device, tenzij de gedeclareerde interface die aanbiedt.
Interfaces kunnen ook het type zijn van parameters, retourwaarden en lijstelementen. Een methode die Chargeable accepteert, kan met een telefoon of een scooter werken zonder de indeling van hun velden te kennen. De Java-bibliotheek gebruikt dezelfde aanpak: List heeft implementaties zoals ArrayList en LinkedList; Runnable declareert run(); Comparable declareert een natuurlijke vergelijking.
Combineer meer dan één functionaliteit
Een klasse breidt één klasse uit en mag meerdere interfaces implementeren. De volgorde is class Phone extends Device implements Chargeable, Connectable. Een interface kan meerdere interfaces uitbreiden: interface SmartDevice extends Chargeable, Connectable. Implementerende klassen moeten dan alle overgeërfde abstracte operaties aanbieden.
Een interface heeft geen constructoren en geen instantievelden. new Chargeable() compileert niet. Een gedeclareerd veld zoals int FULL = 100; is impliciet een public static final constante, te lezen als Chargeable.FULL.
Een abstracte interfacemethode levert geen werkende code. De implementatie kan uit de klasse zelf komen of uit een overgeërfde public superklassemethode. Default-methoden zijn de interfacemethoden die overgeërfd instantiegedrag kunnen leveren.
Belangrijkste punten
- Een interface is een type dat een gemeenschappelijke functionaliteit beschrijft.
- Een concrete implementerende klasse moet geschikte public implementaties aanbieden of overerven.
- Een klasse kan meerdere interfaces implementeren en tegelijk één klasse uitbreiden.
- Interfaces hebben geen constructoren of instantievelden; hun velden zijn constanten.
- Via het interfacetype kunnen aanroepers het contract gebruiken voor niet-verwante klassen.
3.3 Gebruik gemeenschappelijk interfacegedrag en los conflicten op
Interfaces kunnen zowel werkende code als abstracte methodedeclaraties aanbieden. De modifier van de methode bepaalt of implementerende objecten de methode overerven, aanroepers de interfacenaam gebruiken of alleen code binnen de interface de methode kan gebruiken.
Bouw standaardgedrag op uit het contract
interface Chargeable {
int batteryPercent();
void charge(int minutes);
default boolean needsCharge() {
return batteryPercent() < 20;
}
static int clamp(int percent) {
return Math.max(0, Math.min(100, percent));
}
}
Een default-methode is een public instantiemethode met een body. Een telefoon die 15 procent meldt, overerft needsCharge() en geeft waar terug. Bij 20 procent geeft de methode onwaar terug. De default-methode roept de abstracte operatie batteryPercent() aan; die leest geen privaat veld van een telefoon. Een klasse mag de methode overschrijven met een eigen public implementatie.
Een statische interfacemethode hoort bij de interface: Chargeable.clamp(130) geeft 100 terug en Chargeable.clamp(-5) geeft 0 terug. Implementerende klassen overerven de methode niet, dus Phone.clamp(130) is geen geldige aanroep van deze methode. Default-methoden en statische interfacemethoden zijn ingevoerd in Java 8.
Sinds Java 9 mag een interface ook privémethoden declareren om hulpcode tussen haar eigen methoden te delen. Een private instantiehulpmethode ondersteunt default-methoden; een private statische hulpmethode kan ook statische methoden ondersteunen. Implementerende klassen kunnen deze hulpmethoden niet rechtstreeks aanroepen.
Los conflicten tussen default-methoden op
Twee niet-verwante interfaces kunnen dezelfde signatuur aanbieden:
interface Lamp {
default String describe() { return "lamp"; }
}
interface Speaker {
default String describe() { return "speaker"; }
}
class SmartLamp implements Lamp, Speaker {
@Override
public String describe() {
return Lamp.super.describe() + " and "
+ Speaker.super.describe();
}
}
Zonder de overschrijvende methode heeft SmartLamp een conflict en compileert de klasse niet. De gekwalificeerde aanroepen InterfaceName.super.method() selecteren de afzonderlijke default-methoden van de directe superinterfaces. Deze implementatie geeft “lamp and speaker” terug.
| Concurrerende implementaties | Regel |
|---|---|
| Overgeërfde public concrete superklassemethode en default-methode van een interface | De klassemethode wint |
| Default-methode van een interface en van een interface die deze uitbreidt | De specifiekere interface wint |
| Default-methoden van niet-verwante interfaces | De klasse moet het conflict oplossen |
Ook een abstracte declaratie in een superklasse kan vereisen dat een concrete subklasse de methode implementeert; een default-methode van een interface neemt die verplichting niet weg.
Houd rekening met bestaande implementerende klassen
Als je een abstracte methode toevoegt, moet elke concrete implementerende klasse een passende implementatie aanbieden of overerven. Een default-methode voorkomt die wijziging vaak door werkend gedrag te bieden, maar kan een conflict met een andere overgeërfde methode veroorzaken. Default-methoden helpen een interface zich te ontwikkelen; de overervingsregels bepalen nog steeds of de resulterende klassen compileren.
Belangrijkste punten
- Een default-methode biedt overschrijfbaar public instantiegedrag.
- Roep een statische interfacemethode aan via de interfacenaam.
- Privé-interfacemethoden zijn hulpmethoden die alleen binnen de interface zichtbaar zijn.
- Een concrete klassemethode heeft voorrang op een default-methode van een interface.
- Conflicterende default-methoden van niet-verwante interfaces vereisen een overschrijving; anders wint de default-methode van de specifiekere interface.
- Het toevoegen van een default-methode kan conflicten tussen overgeërfde methoden veroorzaken.
3.4 Kies een gemeenschappelijke basis, een functionaliteit of beide
Een abstracte klasse en een interface kunnen allebei vereiste methoden definiëren. De keuze hangt af van de relatie tussen objecten en van de vraag of hun implementatie een gedeelde instantietoestand nodig heeft.
Vergelijk wat beide bieden
| Vraag | Abstracte klasse | Interface |
|---|---|---|
| Kan die velden per object opslaan? | Ja | Nee |
| Kan die constructoren hebben? | Ja | Nee |
| Kan die werkende instantiemethoden aanbieden? | Ja | Ja, met default-methoden |
| Hoeveel kan een klasse er rechtstreeks gebruiken? | Eén superklasse | Meerdere geïmplementeerde interfaces |
| Welke toegangsniveaus zijn beschikbaar? | Alle normale niveaus | Public methoden en private hulpmethoden |
Interfacevelden zijn public static final constanten, geen gegevens per object. Private interfacehulpmethoden ondersteunen de eigen werkende methoden van de interface; ze maken geen private toestand voor elk implementerend object.
Zet nauw verwante toestand in een abstracte klasse
Een tekenprogramma heeft cirkels, rechthoeken en driehoeken. Elke vorm slaat een naam en een positie op en berekent de oppervlakte op een andere manier. Een abstracte Shape kan de gemeenschappelijke velden en constructor bewaren, werkende methoden voor beweging of beschrijving aanbieden en area() abstract declareren.
Alle concrete vormen zijn soorten Shape, dus andere code kan om een vorm vragen en elk van deze vormen ontvangen. Zo kan ook een abstracte Enemy gezondheidspunten en een gemeenschappelijke methode takeDamage(int points) bevatten en de beweging overlaten aan specifieke vijandklassen. De gemeenschappelijke toestand geeft deze basisklassen nuttig werk.
Zet een breed bruikbare functionaliteit in een interface
Hetzelfde tekenprogramma kan vormen, tekstlabels en afbeeldingen exporteren. Exporteren maakt van een tekstlabel geen vorm en deze objecten kunnen al verschillende superklassen nodig hebben. Een interface Exportable kan String exportText() declareren. Elke klasse implementeert de operatie met haar eigen gegevens.
Een klasse mag de twee rollen combineren: class Circle extends Shape implements Exportable. De superklasse biedt de toestand van de vorm en gemeenschappelijke code; de interface maakt de cirkel bruikbaar voor exportcode. Een klasse die meerdere interfaces implementeert, belooft meerdere functionaliteiten zonder instantievelden van die interfaces over te erven.
Combineer een contract met een gedeeltelijke implementatie
Een abstracte klasse kan een interface implementeren en slechts enkele methoden voltooien. Concrete subklassen vullen de resterende operaties aan. List en AbstractList van Java gebruiken deze opzet; ArrayList breidt AbstractList uit. Aanroepers kunnen op de interface vertrouwen terwijl implementerende klassen waar passend een basisklasse hergebruiken.
Met List<String> names = new ArrayList<>(); kunnen aanroepers bijvoorbeeld namen toevoegen, lezen en verwijderen via List. Als je de initialisator vervangt door new LinkedList<>(), blijven die aanroepen werken. Een parameter die als ArrayList<String> is gedeclareerd, zou een LinkedList<String> afwijzen, ook als die alleen lijstoperaties nodig heeft.
Een abstracte klasse met alleen abstracte methoden neemt nog steeds de enige superklasseplaats van de subklasse in. Ze is niet uitwisselbaar met een interface. Kies de interface als alleen het gemeenschappelijke contract nodig is en voeg een basisklasse toe als verwante implementaties baat hebben bij gedeelde toestand of code.
Belangrijkste punten
- Gebruik een abstracte klasse voor verwante soorten objecten die toestand of code delen.
- Gebruik een interface voor een functionaliteit die niet-verwante klassen kunnen bieden.
- Een klasse kan tegelijk een abstracte basisklasse uitbreiden en interfaces implementeren.
- Een gedeeltelijke abstracte implementatie kan een interface ondersteunen zonder aanroepers tot die basisklasse te beperken.
4Schrijf flexibele programma’s met polymorfisme
Het type van een variabele en de klasse van een object beantwoorden verschillende vragen. Het gedeclareerde type van de variabele bepaalt welke aanroepen compileren; de werkelijke klasse van het object bepaalt welke overschrijvende methode wordt uitgevoerd. Door die beslissingen gescheiden te houden, kun je onbekende code volgen, casts veilig gebruiken en lussen schrijven die met nieuwe implementaties werken. Dezelfde aanpak ondersteunt bibliotheekinterfaces zoals Comparable, die objecten een natuurlijke sorteervolgorde geeft.
4.1 Onderscheid het referentietype van de objectklasse
Polymorfisme laat een algemene referentie naar objecten van verschillende subtypen verwijzen. Het object behoudt zijn volledige toestand en gedrag; de referentie geeft aanroepers een bepaalde kijk op de beschikbare operaties.
Benoem de twee typen
Person p = new Student("Ada Lovelace", "S-17");
Het gedeclareerde type, ook statisch type genoemd, is Person: het staat in de variabeledeclaratie. Het werkelijke type, of uitvoeringstype, is Student: dat is de klasse waarvan new een instantie maakt. De werkelijke klasse van het object verandert niet na de constructie.
Een studentreferentie toewijzen aan een persoonsreferentie is een upcast. Die gaat automatisch en is veilig omdat elke student een persoon is. Naar dezelfde student kan ook worden verwezen met een Object-variabele. Een implementerend object kan via upcasting worden toegewezen aan een interface die het implementeert, zoals in Chargeable c = new Phone(35);.
Door de toewijzing worden geen studentgegevens verwijderd. Er is nog steeds één studentobject met zijn studentidentificatie, overgeërfde persoonsgegevens en overschreven methoden. Als je het een algemenere referentie geeft, verandert wat de compiler via die referentie toegankelijk maakt.
Controleer aanroepen aan de hand van het gedeclareerde type
Als Person getName() declareert, compileert p.getName(). Als alleen Student getStudentId() declareert, compileert p.getStudentId() niet. De compiler controleert de publieke interface van Person, ook als de initialisator duidelijk een student maakt.
De omgekeerde toewijzing Student s = p; wordt niet geaccepteerd zonder expliciete typeconversie. Een persoonsreferentie kan verwijzen naar een persoon die geen student is. Die beperking beschermt code tegen het behandelen van een object als een subtype dat het niet heeft.
| Gedeclareerd type | Aanroepen beschikbaar via de referentie |
|---|---|
Object | Public operaties die Object biedt |
Person | Toegankelijke operaties van Person, inclusief overgeërfde operaties |
Student | Toegankelijke studentoperaties en overgeërfde operaties |
Laat parameters en lijsten meerdere subtypen accepteren
Een methode static void printCard(Person person) kan een Person-, Student- of Teacher-object ontvangen, aangenomen dat de laatste twee Person uitbreiden. De code kan vertrouwen op het contract van de persoon. Ook een retourtype kan algemeen zijn, bijvoorbeeld bij een methode die een Shape teruggeeft.
Een lijst met een algemeen elementtype kan verschillende vormklassen opslaan:
List<Shape> shapes = new ArrayList<>();
shapes.add(new Circle(1.5));
shapes.add(new Rectangle(3.0, 4.0));
for (Shape shape : shapes) {
System.out.println(shape.area());
}
Hier komen List en ArrayList uit java.util en biedt elke concrete vorm area() aan. De lus gebruikt alleen die gemeenschappelijke operatie.
Een List<Circle> is geen List<Shape>. Als dat wel zo was, zou code die de lijst als vormenlijst ontvangt, een rechthoek kunnen toevoegen aan een lijst die beloofd is alleen cirkels te bevatten. Als een methode List<Shape> vereist, maak de lijst dan vanaf het begin met dat elementtype.
Belangrijkste punten
- Het gedeclareerde type bepaalt welke operaties via een referentie toegankelijk zijn.
- Het werkelijke type is de klasse van het object en verandert niet na de constructie.
- Upcasting naar een superklasse of geïmplementeerde interface gaat automatisch en is veilig.
- Een algemene parameter of een algemeen lijstelementtype kan meerdere concrete subtypen ondersteunen.
List<Circle>kan niet alsList<Shape>worden gebruikt.
4.2 Volg de selectie van overladen methoden en dynamische binding
Een methodeaanroep wordt in twee fasen afgehandeld. Tijdens de compilatie controleert Java de gedeclareerde typen en kiest het een methodesignatuur. Tijdens de uitvoering kiest dynamische binding de overschrijvende instantiemethode voor de werkelijke klasse van het object.
Volg een aanroep binnen een overgeërfde methode
class A {
String who() { return "A"; }
String hello() { return "Hello from " + who(); }
}
class B extends A {
@Override String who() { return "B"; }
}
class C extends B { }
Voor A x = new C(); is x.hello() toegestaan omdat A hello() declareert. Klasse C overschrijft hello() niet en B doet dat evenmin, dus voert Java de implementatie in A uit.
Binnen die methode betekent who() hetzelfde als this.who(). De ontvanger is nog steeds het C-object. Java zoekt vanaf C en vindt vervolgens de dichtstbijzijnde implementatie van who() in B. Het resultaat is “Hello from B”. Als een overgeërfde methode wordt uitgevoerd, verandert de ontvanger niet in een object van de superklasse.
Kies een overladen methode vóór een overschrijvende methode
static String kind(Object value) { return "object"; }
static String kind(String value) { return "string"; }
Binnen een klasse die deze methoden bevat, kiest Object value = "hi"; kind(value) voor kind(Object) en geeft “object” terug. Het gedeclareerde type van de variabele is Object; Java bekijkt tijdens de uitvoering de overladen methoden niet opnieuw op basis van de werkelijke string.
Bij een instantieaanroep kan dynamische binding nog steeds een overschrijving van de al gekozen signatuur selecteren. Ze kan niet overschakelen naar een andere overladen methodevariant die alleen specifieker lijkt voor het werkelijke argument. Bepaal eerst de signatuur en zoek daarna de overschrijvende methode.
Herken aanroepen en leden die anders worden geselecteerd
| Lid of aanroep | Wat bepaalt de selectie? |
|---|---|
| Overschreven instantiemethode | Werkelijke klasse van de ontvanger, na selectie van de signatuur |
| Statische methode | Type dat tijdens de compilatie voor de aanroep wordt gebruikt |
| Verborgen veld | Gedeclareerd type van de referentie |
| Private methode | Code van de klasse die de methode declareert |
super.method() | Implementatie van de superklasse |
Als P String label = "P"; declareert en Q extends P zijn eigen String label = "Q"; declareert, leest P p = new Q(); p.label “P”. Velden worden verborgen, niet overschreven. Beide velden bestaan in het object. Gebruik overgeërfde veldnamen liever niet opnieuw, omdat het referentietype ervoor kan zorgen dat toegangen die er hetzelfde uitzien verschillende toestand lezen.
super.method() kiest de implementatie van de superklasse, inclusief een implementatie die de superklasse overerft. Als de gekozen methode zelf een gewone aanroep op this doet, kan die interne aanroep nog steeds worden gebonden aan een overschrijvende methode van een subklasse.
Belangrijkste punten
- Tijdens de compilatie wordt de aanroepbare signatuur vastgelegd, inclusief de keuze tussen overladen methoden.
- Dynamische binding kiest de dichtstbijzijnde overschrijving van die signatuur, gezocht vanaf de werkelijke klasse omhoog.
- Een gewone aanroep binnen een overgeërfde methode gebruikt nog steeds de werkelijke ontvanger.
- Velden, statische methoden, private methoden en expliciete
super-aanroepen gebruiken niet de gewone dynamische selectie van overschrijvende methoden.
4.3 Controleer een subtype voordat je de specifieke methoden gebruikt
Een algemene referentie heeft soms toegang nodig tot een operatie die maar bij één subtype hoort. Een downcast vertelt de compiler dat de referentie als dat subtype moet worden behandeld. Tijdens de uitvoering controleert Java of het object al dat type heeft.
Begrijp wat een expliciete typeconversie verandert
Stel dat Person p naar een student verwijst. Student s = (Student) p; maakt s.getStudentId() mogelijk. Beide variabelen verwijzen nog steeds naar hetzelfde object. De conversie kopieert het object niet, verandert de werkelijke klasse niet en voegt geen studentvelden toe aan een gewone persoon.
Als p werkelijk naar new Person("Grace Hopper") verwijst, werpt die conversie ClassCastException op. Een compiler accepteert de mogelijke relatie van persoon naar student, maar het object moet daar tijdens de uitvoering nog steeds aan voldoen. Een expliciete typeconversie tussen klassen die niet verwant kunnen zijn, zoals een conversie van een Person-referentie naar de final-klasse Integer, mislukt tijdens de compilatie.
Dit verschilt van een primitieve conversie: (int) 3.9 maakt de gehele waarde 3. Een expliciete conversie van een referentie controleert en verandert alleen het type waarmee een bestaande referentie wordt gebruikt.
Combineer de test en de nieuwe variabele
Sinds Java 16 maakt patroonherkenning het veilige geval compact:
static void showStudentId(Person person) {
if (person instanceof Student student) {
System.out.println(student.getStudentId());
}
}
De test is waar voor een student of een subklasse van student. De variabele student is beschikbaar binnen het blok, waar bekend is dat de test is geslaagd. Een gewone persoon slaat het blok over. Een null-referentie maakt de test ook onwaar, zodat het blok nooit probeert een methode op null aan te roepen.
De oudere vorm test person instanceof Student en declareert daarna Student student = (Student) person; binnen de tak voor een geslaagde test. Beide vormen beschermen de expliciete typeconversie. Een test van een niet-gerelateerd object elders beschermt een conversie van person niet; de test en de conversie moeten over dezelfde referentie gaan.
Een upcast heeft geen expliciete typeconversie nodig. Person person = student; compileert al en kan niet mislukken door een typeverschil. Een expliciete conversie van null naar een compatibel referentietype levert null op; als je dat resultaat gebruikt voor een aanroep van een instantiemethode, mislukt die nog steeds met NullPointerException.
Bepaal of een typetest nodig is
Typetests passen bij code die een Object ontvangt, zoals equals(Object other), of een operatie nodig heeft die alleen bij een subtype hoort, zoals een studentidentificatie. Ze kunnen ook helpen bij het werken met klassen die niet kunnen worden gewijzigd.
Een herhaalde keten van typetests voor een gemeenschappelijke operatie is een ander probleem. Als code elke vormklasse controleert om haar oppervlakte te berekenen, vereist elke toegevoegde vorm een extra tak. Door area() in Shape te declareren en subklassen die te laten implementeren, kan de aanroeper in plaats daarvan shape.area() gebruiken.
Een expliciete typeconversie kan een ontbrekende gemeenschappelijke operatie niet voor elk subtype beschikbaar maken. Zet gemeenschappelijk gedrag in het gemeenschappelijke contract en bewaar een subtypetest voor werk dat werkelijk dat specifieke subtype vereist.
Belangrijkste punten
- Een downcast controleert een bestaand object; die verandert het object niet.
- Een mogelijke expliciete typeconversie kan compileren en tijdens de uitvoering toch
ClassCastExceptionopwerpen. instanceofis onwaar voor null en waar voor instanties van het type of de subtypen ervan.- Een
instanceof-patroon introduceert een veilig getypeerde variabele waar vaststaat dat de test waar is. - Gemeenschappelijke operaties horen in het gemeenschappelijke type; operaties die alleen bij een subtype horen, kunnen een typetest vereisen.
4.4 Schrijf algemene lussen en definieer een natuurlijke volgorde
Een methode blijft bruikbaar voor nieuwe klassen als haar parameter precies de operaties beschikbaar maakt die de methode nodig heeft. De aanroeper levert objecten; dynamische binding levert hun afzonderlijke gedrag.
Tel oppervlakten op zonder klassespecifieke takken
static double totalArea(List<Shape> shapes) {
double total = 0;
for (Shape shape : shapes) {
total += shape.area();
}
return total;
}
Bij een cirkel met straal 1,5 m, een rechthoek met afmetingen 3,0 m en 4,0 m en een driehoek met basis 4,0 m en hoogte 2,5 m zijn de oppervlakten ongeveer 7,06858, 12,0 en 5,0 vierkante meter. Als je de niet-afgeronde oppervlakte van de cirkel meetelt, krijg je ongeveer 24,07 vierkante meter. Elk object levert zijn eigen area().
Het toevoegen van een klasse Triangle vereist geen wijziging in de lus. De nieuwe klasse implementeert de bestaande vormoperatie. Een negatief resultaat zou het oppervlaktecontract schenden, ook als de overschrijvende methode compileerde; algemene code vertrouwt zowel op de betekenis van de operatie als op haar signatuur.
Dezelfde aanpak werkt met een interface. Een methode chargeAll(List<Chargeable> devices, int minutes) doorloopt apparaten en roept device.charge(minutes) aan. Na 20 minuten bereikt de telefoon die op 35 procent begint 75 procent en de scooter die op 50 procent begint 70 procent. De lus kiest nooit zelf een laadregel.
Laat aanroepers een lijstimplementatie kiezen
Een parameter List<Shape> accepteert zowel een ArrayList<Shape> als een LinkedList<Shape>. Een parameter ArrayList<Shape> wijst de gekoppelde lijst af, hoewel alleen lijstoperaties nodig zijn. Gebruik het meest algemene type dat de vereiste methoden biedt; alles als Object declareren zou te algemeen zijn omdat dat type area() en lijstoperaties mist.
Maak objecten sorteerbaar met Comparable
Comparable<T> definieert de natuurlijke volgorde van een object. Het resultaat van compareTo is negatief als dit object vóór het andere komt, nul als ze dezelfde sorteerrang hebben en positief als het erna komt. Het teken van het resultaat is van belang; het hoeft niet precies −1 of 1 te zijn.
record Runner(String name, int seconds)
implements Comparable<Runner> {
@Override
public int compareTo(Runner other) {
return Integer.compare(seconds, other.seconds);
}
}
Een wijzigbare lijst met Mia op 3.605 s, Ken op 3.490 s en Ana op 3.720 s wordt na Collections.sort(runners) Ken, Mia, Ana. list.sort(null) gebruikt ook de natuurlijke volgorde. Bouw een wijzigbare lijst met new ArrayList<>(List.of(...)); een poging om het niet-wijzigbare resultaat van List.of(...) te sorteren werpt UnsupportedOperationException op.
Gebruik Integer.compare in plaats van de waarden af te trekken. Aftrekken kan bij grote waarden met tegengestelde tekens overlopen en het verkeerde teken teruggeven. Hetzelfde idee geldt voor Double.compare bij kommavelden. In dit voorbeeld met hardlopers hebben gelijke tijden dezelfde sorteerrang, ook als de namen verschillen. Dezelfde sorteerrang en objectgelijkheid hoeven dus niet hetzelfde te betekenen. Geef de voorkeur aan een natuurlijke volgorde waarvan het nulresultaat overeenkomt met gelijkheid; documenteer een bewust verschil.
Belangrijkste punten
- Algemene code roept een gemeenschappelijke operatie aan en laat elk object de implementatie ervan leveren.
- Een parameter van een interfacetype kan verschillende implementaties van hetzelfde contract accepteren.
compareTogebruikt een negatief resultaat, nul of een positief resultaat om de volgorde uit te drukken.Integer.comparevoorkomt het risico op overloop bij het aftrekken van gehele velden.- Sorteren vereist een wijzigbare lijst;
List.of(...)alleen levert een niet-wijzigbare lijst. - Dezelfde sorteerrang kan verschillen van waardegelijkheid als de vergelijking minder velden gebruikt.
5Modelleer vaste waarden en gegevens met enums en records
Sommige programma’s hebben een vaste verzameling keuzemogelijkheden nodig, terwijl andere een kleine groep waarden nodig hebben die bij elkaar horen. Enums geven keuzemogelijkheden een eigen type; records leveren veel van de routinecode voor gegevensobjecten. Beide passen in het typesysteem van Java en kunnen interfaces implementeren. Hun ingebouwde gedrag bespaart werk, maar de keuze tussen deze typen en een gewone klasse hangt nog steeds af van wat kan veranderen en wat twee waarden gelijk maakt.
5.1 Geef vaste keuzemogelijkheden weer met een enum
Een enum geeft een vaste verzameling benoemde keuzemogelijkheden een eigen type. enum Size { SMALL, MEDIUM, LARGE } maakt Size size = Size.MEDIUM; mogelijk. Het voorkomt dat een aanroeper de verkeerd gespelde string “MEDUIM” of het gehele getal 7 doorgeeft aan een methode die Size vereist.
Gebruik namen, constanten en switch
Elke enumconstante is één object, dus size == Size.LARGE is een geschikte vergelijking. Een enumvariabele kan ook null zijn. Size.values() geeft alle constanten in declaratievolgorde terug; Size.valueOf("LARGE") vindt de exacte naam. Het opzoeken is hoofdlettergevoelig: “large” veroorzaakt IllegalArgumentException.
name() geeft de gedeclareerde naam terug. ordinal() geeft de positie terug, geteld vanaf nul, dus Size.MEDIUM.ordinal() is 1. Als je een constante invoegt, veranderen latere posities. Daarom is een volgnummer ongeschikt als opgeslagen code of bedrijfswaarde. Standaard geeft toString() ook de naam terug.
Een switch-expressie, standaard sinds Java 14, kan een waarde teruggeven:
String label = switch (size) {
case SMALL -> "S";
case MEDIUM -> "M";
case LARGE -> "L";
};
Door elke constante te noemen, is de expressie volledig zonder default-tak. Deze gevallen gaan ervan uit dat size niet null is; het opsommen van de constanten handelt null niet af.
Geef elke constante betekenisvolle gegevens
enum CupSize {
SMALL(250), MEDIUM(350), LARGE(500);
private final int milliliters;
CupSize(int milliliters) { this.milliliters = milliliters; }
public int milliliters() { return milliliters; }
}
De puntkomma scheidt de lijst met constanten van de leden. De constructor wordt één keer voor elke constante uitgevoerd wanneer de enum wordt geïnitialiseerd. Hij is impliciet private en aanroepers kunnen geen nieuwe constanten maken met new CupSize(400).
Twee grote bekers en één kleine beker bevatten 1.250 mL, oftewel 1,25 L. Die berekening gebruikt het veld milliliters, niet de posities van de constanten. Als je CupSize.values() doorloopt en elke constante en inhoud afdrukt, krijg je “SMALL: 250 mL”, “MEDIUM: 350 mL” en “LARGE: 500 mL”.
Geef constanten eigen gedrag
Een enum kan een interface implementeren en mag afzonderlijke constanten verschillende methodebody’s geven:
enum Operation {
ADD { int apply(int a, int b) { return a + b; } },
MULTIPLY { int apply(int a, int b) { return a * b; } };
abstract int apply(int a, int b);
}
Operation.MULTIPLY.apply(6, 7) geeft 42 terug. Elke constante vult de abstracte operatie aan. Enums breiden impliciet java.lang.Enum uit; ze kunnen geen andere klasse uitbreiden en gewone klassen kunnen enums niet uitbreiden.
Gebruik een enum voor keuzemogelijkheden die vaststaan wanneer het programma wordt geschreven, zoals richtingen of bestelstatussen. De veranderende productlijst van een winkel hoort bij de gegevens: producten toevoegen zou niet moeten vereisen dat je een enumdeclaratie herschrijft en opnieuw compileert.
Belangrijkste punten
- Een enum geeft een vaste verzameling constanten een afzonderlijk type.
values()behoudt de declaratievolgorde envalueOfzoekt de exacte naam.- Sla betekenisvolle getallen op in velden, omdat volgnummers kunnen veranderen.
- Enumconstructoren initialiseren constanten; aanroepers kunnen
newniet gebruiken om constanten toe te voegen. - Enums kunnen interfaces implementeren en constanten verschillend gedrag geven.
5.2 Bewaar waarden in een record en valideer die
Een record is een beknopte gegevensklasse, standaard sinds Java 16. De kop bevat de waarden, componenten genoemd, die elke instantie bepalen: record Point(int x, int y) { }.
Gebruik de gegenereerde leden
De compiler levert voor elke component een private final-veld, een canonieke constructor met die componenten in volgorde en public accessors die ernaar zijn genoemd. Ook levert de compiler equals, hashCode en toString op basis van de componenten.
Point a = new Point(3, 4);
Point b = new Point(3, 4);
System.out.println(a.x());
System.out.println(a.equals(b));
System.out.println(a == b);
Dit drukt 3, true en false af op afzonderlijke regels. Overeenkomende componentwaarden maken de records gelijk, maar de twee new-expressies maken afzonderlijke objecten. Gelijke records hebben gelijke hashcodes. Hun bruikbare gegenereerde tekst bevat de namen van het record en de componenten; huidige JDK’s tonen “Point[x=3, y=4]”. Dat exacte tekstformaat is niet gegarandeerd, dus parse het niet als gegevensformaat.
Accessors zijn x() en y(), niet getX() en getY(). Een setter die this.x = x toewijst, compileert niet omdat het componentveld final is. Een methode kan in plaats daarvan een nieuwe waarde teruggeven: Point moveRight(int dx) { return new Point(x + dx, y); }.
Valideer in een compacte constructor
record Temperature(double celsius) {
Temperature {
if (celsius < -273.15) {
throw new IllegalArgumentException("below absolute zero");
}
}
}
Een compacte constructor herhaalt de naam van het record zonder parameterlijst. Hij controleert of normaliseert de binnenkomende parameters; Java wijst de componentvelden na de body toe. Deze controle wijst −300 °C af en accepteert 21,5 °C. Een tekstcomponent kan vóór de automatische toewijzing worden genormaliseerd met name = name.trim();.
Een public record heeft een public compacte constructor nodig. Records mogen ook statische velden, statische methoden en instantiemethoden declareren. Een extra constructor geeft de taak met this(...) door aan de canonieke constructor. Extra instantievelden buiten de componenten zijn verboden.
Bescherm veranderlijke componentobjecten
Final componentvelden maken de onveranderlijkheid ondiep. Een record dat een veranderlijke lijst bevat, verwijst nog steeds naar die lijst:
record Team(String name, List<String> members) {
Team {
members = List.copyOf(members);
}
}
Als java.util.List is geïmporteerd, slaat dit een niet-wijzigbare kopie op. Latere toevoegingen aan de oorspronkelijke lijst beïnvloeden het lidmaatschap van het team niet en team.members().add("Lee") werpt UnsupportedOperationException op. De kopie beschermt de lijststructuur; ze maakt veranderlijke elementobjecten niet diep onveranderlijk.
Records breiden impliciet java.lang.Record uit en zijn final. Ze kunnen geen andere klasse uitbreiden en niet worden uitgebreid, maar kunnen interfaces zoals Comparable implementeren. Gebruik ze voor gegevens die door hun waarden worden bepaald; gebruik een gewone klasse voor veranderende toestand, extra verborgen instantievelden of een uitbreidbare klassenhiërarchie.
Belangrijkste punten
- Een recordkop definieert componenten en de gegenereerde constructor, accessors en waardemethoden.
- Recordaccessors gebruiken componentnamen zoals
x(). - Een compacte constructor controleert of normaliseert parameters vóór automatische toewijzing aan velden.
- Referenties in recordcomponenten zijn final, maar objecten waarnaar ze verwijzen kunnen veranderlijk blijven.
- Een niet-wijzigbare kopie van een lijst beschermt de lijststructuur van een record.
- Een record is final, kan niet expliciet een klasse uitbreiden en kan interfaces implementeren.
Overerving in Java: voorbeelden
1Bouw stap voor stap een apparaat met overerving
12 stappen
- 1IntroductieEén object met een algemene en een specifieke rol
Een sensor is een soort apparaat. Elk apparaat heeft een label nodig, terwijl een sensor ook een meeteenheid nodig heeft. Met overerving beheert het apparaat zijn label en voegt de sensor zijn eenheid toe.
We bouwen die relatie op, gebruiken een overgeërfde methode en volgen hoe constructoraanroepen elk deel initialiseren. De belangrijkste vraag is welke klasse elk veld beheert en dus welke constructor er een waarde aan moet toewijzen.
- 2VoorbeeldBegin met de toestand die elk apparaat nodig heeft
class Device { private final String label; Device(String label) { this.label = label; } public String label() { return label; } }Devicebeheert zijn private label. Andere code leest het vialabel(). Een subklasse zal die public methode overerven, maar kan niet rechtstreeks een waarde aan het private veld toewijzen. Zo blijft de initialisatie van het label op één plek. - 3VoorbeeldVoeg alleen de sensorspecifieke toestand toe
class Sensor extends Device { private final String unit; Sensor(String label, String unit) { super(label); this.unit = unit; } public String unit() { return unit; } }Sensorbreidt één klasse uit en voegt één veld toe. De aanroepsuper(label)geeft de verantwoordelijkheid voor het label door aanDevice; de sensorconstructor slaat de eenheid zelf op. - 4KernpuntLees beide delen via de sensor
Na
Sensor s = new Sensor("Room", "C");geefts.label()“Room” terug ens.unit()“C”. De eerste methode is overgeërfd; de tweede hoort bijSensor.Beide aanroepen gaan over hetzelfde object. Door
Deviceuit te breiden is geen tweede apparaatobject naast de sensor gemaakt. Het private label is aanwezig, ook al benadert sensorcode het via een methode. - 5UitdagingKies de relatie voor een laboratorium
Een
Labbevat meerdere sensoren. MoetLabSensoruitbreiden of referenties naar sensoren bewaren? Leg de relatie uit.---reveal---
Bewaar sensorreferenties in velden of een collectie. Een laboratorium heeft sensoren maar is zelf geen sensor, dus compositie past. Als het laboratorium
Sensorzou uitbreiden, zou dat betekenen dat het overal kan worden gebruikt waar een sensor vereist is, ook bij operaties die de eenheid van één sensor beschrijven. - 6UitlegGa de superklasseconstructor binnen
De aanroep
new Sensor("Room", "C")kiest de sensorconstructor met twee argumenten. De aanroepsuper(label)daarin roeptDevice(String)aan voor datzelfde object.Devicebereikt eerst zijn eigen superklasse,Object, en wijst daarna het label toe. WanneerDeviceterugkeert, worden de initialisatie van sensorvelden en de rest van de sensorconstructorbody uitgevoerd. Tot slot komt de nieuwe referentie beschikbaar voor de aanroeper. - 7TipZoek de ontbrekende constructor zonder parameters
Verwijder
super(label)uit de sensorconstructor en Java voegtsuper()in. Voor die aanroep is geen passende constructor:Devicedeclareert alleenDevice(String).De constructorfout gaat over argumenten, niet over het private veld. Als je de aanroep vervangt door
new Device(label), maak je een niet-gerelateerd object en blijft het probleem met de implicietesuper()bestaan. Geef het label in plaats daarvan door aan de superklasseconstructor. - 8VoorbeeldDelegeer voor een optionele standaardeenheid
Voeg deze constructor toe binnen
Sensor:Sensor(String label) { this(label, "unknown"); }new Sensor("Hall")geeft de taak door aan de bestaande constructor met twee argumenten, diesuper(label)aanroept. Het resulterende object heeft label “Hall” en eenheid “unknown”. Schrijf niet ooksuper(...)in de delegerende constructor: één constructor heeft hoogstens één expliciete constructoraanroep. - 9VerbandValideer een parameter vóór de constructie
Standaard Java 25 staat beperkt werk toe vóór een expliciete constructoraanroep. Zo kan een sensorconstructor een binnenkomend leeg label afwijzen voordat hij
super(label)aanroept.Die vroege code mag parameters gebruiken, maar kan geen velden van het object lezen of de instantiemethoden ervan aanroepen. Beperkte eenvoudige toewijzingen aan velden van de huidige klasse zonder initialisator bij de declaratie zijn ook toegestaan. Houd de aanroep bij standaardtaalversies van oudere versies als eerste.
- 10UitdagingVoorspel een constructorbericht
In een afzonderlijke versie drukt
Device“device” af na de eigen superklasseaanroep.Sensordrukt “sensor” af nasuper(label). Welk bericht verschijnt als eerste bij het maken van een sensor en waarom?---reveal---
“device” verschijnt als eerste. De sensorconstructor roept de apparaatconstructor aan voordat hij doorgaat naar zijn eigen afdrukinstructie. Het object is tijdens de hele constructie een sensor; deze berichten tonen de volgorde van het constructorwerk, geen veranderingen in de klasse.
- 11UitdagingHerstel een toewijzing aan een overgeërfd veld
Een nieuwe subklasse probeert het private apparaatlabel te initialiseren met
this.label = label;en laatsuper(label)weg. Welke twee samenhangende problemen veroorzaakt dit?---reveal---
De subklasse kan niet rechtstreeks een waarde aan het private veld toewijzen. Ook is er geen passende apparaatconstructor voor de impliciete
super(). Gebruiksuper(label)zodatDevicezijn eigen private toestand toewijst en wijs daarna alleen waarden toe aan velden die in de subklasse zijn gedeclareerd. - 12SamenvattingZoek de oorzaak van een constructiefout
Als een subtype niet kan worden geconstrueerd, zoek dan de eerste constructoraanroep waarvoor geen passend toegankelijk doel bestaat. Controleer daarna of elke klasse de velden initialiseert die ze beheert.
Houd naast dat verloop ook de relatietest aan: een subtype is bruikbaar als zijn basistype; een ingesloten object hoort in een veld. Deze controles richten zich op verschillende soorten ontwerp- en compilerfouten.
2Druk catalogusitems correct af en vergelijk ze
14 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieBepaal wat een catalogusitem moet betekenen
- 2VoorbeeldBreid een basisbeschrijving uit
- 3TipVoorkom dat de overschrijvende methode zichzelf aanroept
- 4UitdagingControleer een schijnbare vervanging
- 5VoorbeeldGeef het afdrukken een exacte methode om aan te roepen
- 6KernpuntLees de werkelijke klasse zonder die te veranderen
- 7UitdagingOnderzoek afgedrukte tekst die op de standaarduitvoer lijkt
- 8VoorbeeldDeclareer de gegevens voor gelijkheidsbepaling
- 9VoorbeeldWijs het verkeerde object af voordat je velden vergelijkt
- 10VoorbeeldBereken de hashcode uit dezelfde velden voor gelijkheidsbepaling
- 11UitdagingHerken de overladen methodevariant die een collectie mist
- 12KernpuntBescherm vergelijkingen in beide richtingen
- 13UitdagingOnderscheid gelijke tekst van gelijke waarden
- 14SamenvattingControleer een waardeklasse via haar publieke interface
3Bouw een contract en deel de implementatie
18 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieOnderscheid de gemeenschappelijke gegevens van de gemeenschappelijke functionaliteit
- 2VoorbeeldBegin een abstracte berichtenbron
- 3VoorbeeldVul de ontbrekende operatie aan
- 4UitdagingBepaal of een subklasse concreet kan zijn
- 5VoorbeeldBenoem lezen als functionaliteit
- 6VerbandLaat de bestaande basisklasse aan de interface voldoen
- 7VoorbeeldSchrijf een aanroeper voor het contract
- 8UitdagingHerstel een implementatie met beperktere toegang
- 9VoorbeeldVoeg een bruikbare default-operatie toe
- 10UitlegHoud statische en private hulpmethoden bij hun rol
- 11VoorbeeldMaak een werkelijk conflict tussen default-methoden
- 12VoorbeeldKies expliciet een default-methode
- 13UitdagingGeef de klasse-implementatie voorrang
- 14KernpuntBied een basis zonder elke implementatie tot gebruik te verplichten
- 15UitdagingPlaats een label en een constructor in het juiste type
- 16TipControleer nieuwe default-methoden naast overgeërfde methoden
- 17UitdagingVoeg een nieuwe lezer toe zonder de bestaande superklasse te veranderen
- 18SamenvattingBeoordeel een voorgesteld gemeenschappelijk type
4Volg een rapportaanroep via algemene typen
17 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieVolg wat de variabele belooft en wat het object doet
- 2VoorbeeldGeef een gespecialiseerd rapport een algemene referentie
- 3UitlegControleer wat via de referentie beschikbaar is
- 4UitdagingKies typen voor een gemengde rapportenlijst
- 5VoorbeeldBepaal eerst welke methode de buitenste aanroep uitvoert
- 6VoorbeeldBepaal welke methode de binnenste aanroep op dezelfde ontvanger uitvoert
- 7VoorbeeldKies een overladen methode op basis van het gedeclareerde argumenttype
- 8UitdagingOnderscheid twee beslissingen in één aanroep
- 9TipMarkeer velden en statische aanroepen afzonderlijk in een uitvoeringsverloop
- 10VoorbeeldVraag veilig om de operatie die alleen bij verkooprapporten hoort
- 11UitdagingVoorspel een expliciete typeconversie als het object verandert
- 12VoorbeeldVerzamel kopteksten via de gemeenschappelijke operatie
- 13VerbandGebruik vergelijking als een andere gemeenschappelijke operatie
- 14UitdagingSorteer waarden met een gekozen lijstimplementatie
- 15KernpuntGebruik subtypetests voor subtypespecifiek werk
- 16UitdagingOndersteun een auditkoptekst zonder nieuwe tak
- 17SamenvattingVolg een onbekende aanroep in volgorde
5Bouw een vaste keuze en een beschermde gegevenswaarde
15 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieKies tussen benoemde opties en gegroepeerde gegevens
- 2VoorbeeldGeef servicekeuzes een eigen type
- 3VoorbeeldKoppel bruikbare getallen aan de keuzemogelijkheden
- 4TipHoud de servicegegevens onafhankelijk van de declaratievolgorde
- 5UitdagingVoorspel het resultaat van opzoeken en een bruikbaar veld
- 6VerbandHoud gedrag bij de vaste keuze
- 7VoorbeeldDeclareer de componenten van een leveringsverzoek
- 8VoorbeeldNormaliseer binnenkomende gegevens vóór de toewijzing
- 9UitdagingVergelijk de genormaliseerde verzoeken
- 10VoorbeeldZoek het veranderlijke object binnen een final-component
- 11VoorbeeldSla een niet-wijzigbare kopie op
- 12TipGeef een nieuw record terug als waarden moeten veranderen
- 13UitdagingKies de juiste oplossing voor een veranderende route
- 14UitdagingModelleer nieuwe serviceopties en klantverzoeken
- 15SamenvattingOnderzoek gegenereerd gedrag voordat je code toevoegt
Overerving in Java: oefenvragen
Set 1: Bouw klassen en volg overgeërfd gedrag10 Oefenvragen
Vraag 1 van 10
Library slaat meerdere Shelf-objecten op en elke boekenplank bevat boeken. Hoe moet Library de relatie met Shelf weergeven?Toon antwoordVerberg antwoord
Shelf zou uitbreiden, zou dat ook betekenen dat elke bibliotheek een boekenplank is en overal kan worden gebruikt waar een boekenplank wordt verwacht. Het hergebruiken van plankmethoden maakt die is-een-relatie niet waar.Vraag 2 van 10
Account declareert een privaat veld balance en een public methode getBalance(). SavingsAccount extends Account. Hoe kan een methode in SavingsAccount de overgeërfde rekeningtoestand lezen?Toon antwoordVerberg antwoord
Account lezen. De private toestand hoort nog steeds bij het spaarrekeningobject, maar code in de subklasse kan dat veld niet rechtstreeks benaderen. Door super. toe te voegen, omzeil je de regel voor private toegang niet.Vraag 3 van 10
Wat gebeurt er als deze code wordt gecompileerd?
class Machine {
Machine(int serial) { }
}
class Printer extends Machine {
Printer() { }
}
Toon antwoordVerberg antwoord
super() aan. Machine declareert alleen een constructor die een geheel getal ontvangt, dus er bestaat geen constructor zonder parameters. Java verzint geen argumentwaarde; Printer() kan zonder parameters blijven als de body expliciet een serienummer aan super doorgeeft.Vraag 4 van 10
Wat wordt afgedrukt door new Leaf();?
class Root {
Root() { System.out.print("R"); }
}
class Branch extends Root {
Branch() { System.out.print("B"); }
}
class Leaf extends Branch {
Leaf() { this(7); System.out.print("L"); }
Leaf(int n) { System.out.print(n); }
}
Toon antwoordVerberg antwoord
Leaf() geeft de taak door aan Leaf(int), waarvan de impliciete superklasseaanroep eerst Branch en daarna Root binnengaat. Hun body’s drukken “R” en “B” af, daarna drukt Leaf(int) 7 af en drukt de delegerende constructor “L” af. Door de delegatie wordt het rootdeel niet voor een tweede keer geconstrueerd.Vraag 5 van 10
Meter declareert public int read(). Een subklasse schrijft @Override public int read(int offset). Waarom mislukt de compilatie?Toon antwoordVerberg antwoord
read in plaats van die te overschrijven. @Override vangt het verschil op; het retourtype int is verenigbaar met de oorspronkelijke methode.Vraag 6 van 10
Wat drukt de laatste instructie af?
class Parcel {
public String toString() { return "parcel"; }
}
class ChilledParcel extends Parcel {
@Override public String toString() {
return super.toString() + " cold";
}
}
Parcel item = new ChilledParcel();
System.out.println("Send " + item);
Toon antwoordVerberg antwoord
toString() en het werkelijke object voor een gekoeld pakket voert zijn overschrijvende methode uit. Die methode haalt “parcel” op via super.toString() en voegt “ cold” toe. Als je de referentie als Parcel declareert, onderdruk je de overschrijvende methode niet.Vraag 7 van 10
Label heeft alleen public boolean equals(Label other), die haar tekst vergelijkt, en overerft equals(Object). Twee afzonderlijke labels hebben dezelfde tekst. Beide referenties zijn opgeslagen in variabelen die als Object zijn gedeclareerd. Wat geeft a.equals(b) terug?Toon antwoordVerberg antwoord
Object-referenties kiest de aanroep equals(Object), die Label niet heeft overschreven. De overgeërfde implementatie vergelijkt de twee afzonderlijke objecten op identiteit en geeft onwaar terug. De gelijknamige methode die Label ontvangt, is een overladen methodevariant en wordt niet gekozen op basis van de klasse van het argument tijdens de uitvoering.Vraag 8 van 10
abstract class Sensor declareert public abstract double reading();. Wat moet een concrete LightSensor extends Sensor aanbieden of overerven?Toon antwoordVerberg antwoord
Sensor belooft. Een statische methode is geen overschrijvende instantiemethode en een private methode zou de vereiste toegang beperken.Vraag 9 van 10
Waarom compileert deze klasse niet?
interface Resettable { void reset(); }
class Counter implements Resettable {
void reset() { }
}
Toon antwoordVerberg antwoord
reset() van de interface is impliciet public. De implementatie heeft pakkettoegang, waardoor ze de beloofde toegang beperkt en de compilatie mislukt. Een klasse implementeert een interface; als je de relatie verandert in extends, herstel je de methodedeclaratie niet.Vraag 10 van 10
Wat geeft new TimedJob().cost() terug?
class Job {
public int rate() { return 3; }
public int cost() { return 4 * rate(); }
}
class TimedJob extends Job {
@Override public int rate() { return 5; }
@Override public int cost() { return super.cost() + 2; }
}
Toon antwoordVerberg antwoord
super.cost() naar Job.cost(). De gewone aanroep van rate() daarin wordt nog steeds uitgevoerd op het object van de getimede taak en geeft dus 5 terug; de overgeërfde berekening geeft 20 en de buitenste overschrijvende methode telt er 2 bij op. Als je 3 voor de binnenste aanroep gebruikt, schakel je dynamische binding ten onrechte uit voor de hele superklassemethode.Set 2: Gebruik contracten en algemene referentietypen10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
TextDocument en een niet-verwante klasse Image moeten allebei exportText() ondersteunen. Elk breidt al een andere klasse uit. Welk gemeenschappelijk type geeft deze functionaliteit het best weer?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Compileert deze code en waarom?
interface Named { String name(); }
class Entity {
public String name() { return "entity"; }
}
class Asset extends Entity implements Named { }
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Wat geeft new Screen().label() terug?
interface Tagged {
default String label() { return "tagged"; }
}
class Panel {
public String label() { return "panel"; }
}
class Screen extends Panel implements Tagged { }
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Fast en Safe zijn niet-verwante interfaces, elk met default String mode(). Een concrete Service implementeert beide en overerft geen klasse-implementatie van mode(). Wat moet Service doen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Robot implementeert Movable. Wat is in Movable unit = new Robot(); het gedeclareerde type van unit?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Vehicle heeft move() en alleen Bicycle heeft ringBell(). Bicycle extends Vehicle. Wat gebeurt er met v.ringBell() na Vehicle v = new Bicycle();?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
interface Percent declareert static int cap(int n) en Gauge implements Percent declareert geen methode cap. Hoe moet code de interfacehulpmethode met 120 aanroepen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
area() aan. Ze moet zowel ArrayList<Shape> als LinkedList<Shape> kunnen accepteren. Welk parametertype biedt de benodigde lijstoperaties?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Wat gebeurt er bij het compileren en uitvoeren van new Row().render()?
interface Renderable { String render(); }
abstract class Base implements Renderable {
public String render() { return "[" + body() + "]"; }
protected abstract String body();
}
class Row extends Base {
@Override protected String body() { return "row"; }
}
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Report met private titeltoestand, een constructor die die toestand instelt en een abstracte body(). Rapporten en niet-verwante grafiekobjecten moeten ook afdrukbaar zijn. Welke structuur behoudt beide vereisten?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 3: Volg aanroepen, verborgen leden en gecontroleerde expliciete typeconversies10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Shape s = null; volgt s instanceof Circle. Circle extends Shape. Wat is het resultaat van de test?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Wat gebeurt er met de laatste instructie?
class Animal { }
class Fox extends Animal { }
Animal a = new Animal();
Fox f = (Fox) a;
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Wat geeft pick(value) terug?
static String pick(Object x) { return "object"; }
static String pick(CharSequence x) { return "sequence"; }
// Inside a method of the class containing these overloads:
Object value = "notes";
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Welke tekst levert p.label + ":" + p.show() op?
class Parent {
String label = "P";
String show() { return label; }
}
class Child extends Parent {
String label = "C";
@Override String show() { return label; }
}
Parent p = new Child();
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
List<Vehicle> en mag een bus toevoegen. Bicycle extends Vehicle en Bus extends Vehicle. Waarom kan een List<Bicycle> er niet aan worden doorgegeven?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Wat geeft b.test() terug?
class Base {
String use(Object x) { return "BO"; }
String use(String x) { return "BS"; }
}
class Sub extends Base {
@Override String use(Object x) { return "SO"; }
@Override String use(String x) { return "SS"; }
String test() {
Object text = "x";
return super.use(text) + ":" + use(text);
}
}
Sub b = new Sub();
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Wat drukt new Sub(); af met standaard Java 25?
class Base {
Base() { System.out.print(value()); }
int value() { return -1; }
}
class Sub extends Base {
int n = 8;
Sub() { super(); n = 11; }
@Override int value() { return n; }
}
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Wat wordt door deze code afgedrukt? Tool declareert geen methoden.
class Tool { }
class Drill extends Tool {
int speed() { return 6; }
}
Tool tool = new Drill();
Object copy = tool;
if (copy instanceof Drill d) {
System.out.print(d.speed());
}
System.out.print(":" + (copy == tool));
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Creator heeft public Object create(). TextCreator extends Creator overschrijft die methode. Welke combinatie van retourtype en toegang is geldig?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Document. De voorbeeldweergavemethode gebruikt instanceof-takken om het aantal woorden van elk document te berekenen. Er worden vaak nieuwe documentklassen toegevoegd. Welke wijziging laat nieuwe klassen de voorbeeldweergave ondersteunen zonder die keten van typetests te bewerken?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 4: Modelleer waarden, vergelijk en sorteer ze10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
enum Status { NEW, SENT, DONE }. Wat doet Status.valueOf("sent")?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
record Reading(int count, String unit) { } de telling uit een variabele r?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
enum Band { LOW(2), HIGH(9); ... } slaat het constructorargument op in een onveranderlijk veld code. Een nieuwe constante MID(5) wordt tussen LOW en HIGH ingevoegd. Hoe veranderen de ordinal() en opgeslagen code van HIGH?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Token-objecten geven dezelfde hashcode terug. Hun implementaties van equals(Object) vergelijken correct de velden die elke token bepalen. Schendt de gedeelde hashcode het contract voor gelijkheid en hashcodes?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
record Result(int seconds) implements Comparable<Result> geeft Integer.compare(seconds, other.seconds) terug. Welk teken heeft new Result(418).compareTo(new Result(405))?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Wat drukt de laatste instructie af?
record Tag(String text) {
Tag { text = text.trim(); }
}
Tag a = new Tag(" north ");
Tag b = new Tag("north");
System.out.println(a.equals(b));
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Ticket.equals accepteert elke Ticket met dezelfde code. PriorityTicket extends Ticket accepteert alleen PriorityTicket-objecten en vergelijkt ook de prioriteit. Een gewoon ticket t en een prioriteitsticket p hebben dezelfde code. Wat geven t.equals(p) en p.equals(t) terug en voldoet dat aan symmetrie?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Wat wordt afgedrukt? List en ArrayList zijn geïmporteerd.
record Batch(List<String> names) { }
var original = new ArrayList<String>(List.of("Rin"));
Batch first = new Batch(original);
Batch second = new Batch(new ArrayList<>(original));
original.add("Sol");
System.out.println(first.equals(second) + ":"
+ first.names().size() + ":" + second.names().size());
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Wat gebeurt er wanneer Collections.sort(list) wordt uitgevoerd? De benodigde typen uit java.util zijn geïmporteerd.
record Rank(int score) implements Comparable<Rank> {
public int compareTo(Rank other) {
return Integer.compare(score, other.score);
}
}
List<Rank> list = List.of(new Rank(8), new Rank(3));
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Wat geeft Rule.DOUBLE.apply(Rule.NEXT.apply(4)) terug?
enum Rule {
NEXT {
int apply(int n) {
return switch (n) { case 4 -> 6; default -> n + 1; };
}
},
DOUBLE { int apply(int n) { return 2 * n; } };
abstract int apply(int n);
}
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 5: Combineer Java-regels en onderzoek fouten10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Wat geeft new Terminal().name() terug?
interface Basic {
default String name() { return "basic"; }
}
interface Detailed extends Basic {
default String name() { return "detail"; }
}
class Terminal implements Basic, Detailed { }
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
List<Notifier> bevat objecten van verschillende niet-verwante klassen die Notifier.send() implementeren. Een lus roept n.send() aan op elk element. Een nieuwe PushNotifier implementeert hetzelfde contract correct. Welke wijziging is in de lus nodig om instanties daarvan te ondersteunen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Wat wordt afgedrukt met standaard Java 25?
class Base {
Base(int n) { System.out.print("B" + n); }
}
class Sub extends Base {
Sub(int n) {
if (n < 0) throw new IllegalArgumentException();
super(n + 1);
System.out.print("S" + n);
}
}
new Sub(2);
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Code definieert gelijkheid en hashcodes met behulp van haar tekstveld. Een HashSet<Code> bevat c met de tekst “red”. Het programma moet de tekst veranderen in “blue”, hetzelfde object behouden en betrouwbaar opzoeken in de verzameling mogelijk houden. In welke volgorde moet het programma het object en de verzameling bijwerken?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Wat levert x.call(arg) + ":" + x.call("x") op?
class Alpha {
String call(Object x) { return "AO"; }
String call(String x) { return "AS"; }
}
class Beta extends Alpha {
@Override String call(Object x) { return "BO"; }
@Override String call(String x) { return "BS"; }
}
Alpha x = new Beta();
Object arg = "data";
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Wat drukt de laatste instructie af? List en ArrayList zijn geïmporteerd.
record Group(List<String> names) {
Group { names = List.copyOf(names); }
}
var original = new ArrayList<String>();
original.add("Uma");
Group group = new Group(original);
original.add("Zed");
System.out.println(group.names().size() + ":" + original.size());
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Wat gebeurt er als deze code wordt gecompileerd?
interface Ready {
default String state() { return "ready"; }
}
abstract class Base {
public abstract String state();
}
class Console extends Base implements Ready { }
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
record Player(String name, int points) hebben verschillende namen maar hetzelfde aantal punten. Hun zelfgeschreven compareTo vergelijkt alleen punten met Integer.compare. Wat melden equals en compareTo voor deze twee objecten?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
interface Storable { String saveText(); } aan. Veel nieuwe implementaties delen een privaat padveld, constructorvalidatie en hulpcode, terwijl sommige bestaande implementaties andere klassen uitbreiden. Hoe kan de bibliotheek hergebruik aanbieden en tegelijk de gemeenschappelijke publieke interface behouden?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Kom je niet uit je eigen opgave? Huiswerkhulp
Kernbegrippen
A
Aanroep van een superklasseconstructor
super(...) om argumenten door te geven aan een constructor van de directe superklasse voor het huidige object.Person, dat het private naamveld beheert.Abstracte klasse
Shape kan een naam opslaan en tegelijk vereisen dat concrete cirkels en rechthoeken een oppervlakte leveren.Abstracte methode
area() voorkomt dat een onvoltooide concrete vorm ongemerkt een tijdelijke invulling met oppervlakte nul gebruikt.Accessor
count is count(), dus getCount() is niet beschikbaar tenzij die expliciet is gedeclareerd.B
Beschermde toegang
C
Canonieke constructor
Point(int x, int y) accepteert de x-coördinaat gevolgd door de y-coördinaat.ClassCastException
Animal-object expliciet wordt geconverteerd naar de specifiekere subklasse Fox.Compacte constructor
Comparable
compareTo.Comparable<Runner> zodat een sorteermethode hardlopers op eindtijd kan ordenen.Compositie
Customer een Address bevatten zonder dat elke klant als adres bruikbaar wordt.Concrete klasse
Rectangle is een concrete klasse zodra ze de oppervlakteoperatie aanvult die haar abstracte vormbasisklasse vereist.Concrete methode
Shape.describe() kan de naam hergebruiken en de implementatie van area() van elke subklasse aanroepen.Conflict tussen default-methoden
SmartLamp de overeenkomstige beschrijvingen van Lamp en Speaker overschrijft.Constantespecifieke klassebody
MULTIPLY een operatie anders implementeren dan de constante ADD van de enum.Constructorketen
Covariant retourtype
String teruggeeft wanneer de superklassemethode Object teruggeeft.D
default-methode
needsCharge() kan batteryPercent() gebruiken zonder de indeling van private velden van een apparaat te kennen.Downcasting
Dynamische binding
hello() die who() aanroept, B.who() bereiken wanneer de ontvanger bij klasse B hoort.E
Enkelvoudige overerving
Student als Teacher als directe superklasse uitbreidt.Enumconstante
CupSize.LARGE kan een inhoud van 500 mL opslaan en op basis van identiteit worden vergeleken.F
final-methode
Flexibele constructorbody
Functionaliteit
G
Gedeclareerd type
Person p = new Student(...) is het gedeclareerde type Person, dus methoden die alleen bij studenten horen zijn niet rechtstreeks beschikbaar.Gedeeltelijke implementatie
Storable kan padvalidatie delen terwijl concrete klassen de opgeslagen tekst leveren.Gelijke sorteerrang
compareTo nul teruggeeft, wat betekent dat twee objecten volgens de gekozen sorteerregel dezelfde vergelijkingsrang innemen.Gelijkheidscontract
Gemeenschappelijke toestand
H
Hashbotsing
Hashcode
I
Identiteit
a == b onwaar maakt.Implementatie
Entity is overgeërfd, kan voor een subklasse Asset voldoen aan Named.name().instanceof
instanceof-test kan code controleren of een persoon het studenttype heeft voordat een methode wordt gebruikt die alleen bij studenten hoort.Interface
Chargeable laat een telefoon en een scooter dezelfde aanroepen ondersteunen terwijl ze met verschillende snelheden opladen.Interfaceconstante
Chargeable.FULL kan 100 bevatten terwijl elk apparaat zijn eigen veranderende batterijveld opslaat.Interfacetype
List als interfacetype te gebruiken, kan één methode zowel lijsten op basis van arrays als gekoppelde lijsten accepteren.M
Methodesignatuur
Object naar String verandert, verandert de methodesignatuur en ontstaat een overladen methodevariant.N
Natuurlijke volgorde
Niet-wijzigbare kopie
List.copyOf beschermt de ledenlijst van een team tegen toevoegingen aan de veranderlijke lijst van de aanroeper.O
Object
Object-variabele kan een student of een array bevatten en beide ondersteunen de methoden die elk object heeft.Ondiepe onveranderlijkheid
Ontvanger
shape.area() is de ontvanger het vormobject, ook wanneer de variabele een abstract gedeclareerd type heeft.Opsommingstype
NEW, SENT en DONE.Overerving
Student Person.getName() hergebruiken en tegelijk eigen studiepunten en studiegedrag toevoegen.Overladen
print(String text) naast print(int number) worden toegevoegd zonder een van beide methoden te vervangen.Overloop bij gehele getallen
Integer.compare voorkomt die vergelijkingsfout.Override-annotatie
@Override, laat de compiler controleren of een methode een overgeërfde methode overschrijft of implementeert.tostring() af wanneer de programmeur toString() wilde vervangen.Overschrijven
yearlyPay() te overschrijven kan een manager een bonus toevoegen terwijl aanroepers de werknemersoperatie blijven gebruiken.P
Patroonvariabele
student in person instanceof Student student maakt de identificatiemethode van het overeenkomende object veilig beschikbaar.Polymorfisme
area() aanroepen op cirkels, rechthoeken en driehoeken zonder afzonderlijke typetakken.Privé-interfacemethode
R
Record
Point(int x, int y) groepeert twee coördinaten en biedt gelijkheidsbepaling op basis van componenten zonder handgeschreven methoden.Recordcomponent
seconds kan uit een hardloper worden gelezen met de gegenereerde methode runner.seconds().S
Specifiekere interface
Detailed levert de gekozen name() wanneer ze Basic uitbreidt en de default-methode daarvan vervangt.Standaardconstructor
super() in de body.Person(String name) toe te voegen, verdwijnt de standaardconstructor waarop een subklasse mogelijk vertrouwde.Statische interfacemethode
Chargeable.clamp(130) kan 100 teruggeven zonder een apparaatobject te maken.Subklasse
GraduateStudent kan voortbouwen op Student en nog steeds worden gebruikt waar een Person wordt verwacht.Superinterface
Chargeable is een superinterface van SmartDevice wanneer die laatste haar samen met Connectable uitbreidt.Superklasse
Person is de superklasse van Student, dus een methode die een persoon accepteert, kan ook een student ontvangen.switch-expressie
Symmetrie
book.equals(ebook) waar is maar ebook.equals(book) voor dezelfde objecten onwaar is.T
Tekstrepresentatie
toString() voor een object teruggeeft en die wordt gebruikt wanneer het object wordt afgedrukt of met andere tekst wordt samengevoegd.Transitiviteit
U
Upcasting
Chargeable behoudt de batterijgegevens maar biedt alleen de operaties die via dat type beschikbaar zijn.V
Veldinitialisator
Verbergen van methoden
Verbergen van velden
p.label het veld van de ouderklasse lezen terwijl p.show() een overschrijving in de kindklasse uitvoert die een ander label gebruikt.Volgnummer
LOW, HIGH verandert het volgnummer van HIGH van 1 naar 2 wanneer MID ervoor wordt ingevoegd.W
Waardegelijkheid
Werkelijk type
Person-referentie kan naar een student verwijzen van wie het werkelijke type na de upcast Student blijft.Werkelijke klasse
getClass() meldt, onafhankelijk van het gedeclareerde type van de referentie.Student, ook als een methode die via een parameter van het type Person ontvangt.- 1Eerste Java-programma’s
- 2Voorwaarden en lussen in Java
- 3Java-methoden
- 4Lijsten, arrays en strings in Java
- 5Java-klassen en objecten
- 6Objecten, referenties en gelijkheid in Java
- 7HashMaps en collecties in Java
- 8Overerving in Java
- 9Excepties en bestanden in Java
- 10Klassendiagrammen en unittests in Java
- 11Lambda-expressies en streams in Java
Over deze les
Door StudeliFout melden
Maak je eigen Studiemateriaal
Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.