Hoi! In eerdere lessen hebben we interfaces ontmoet en ontdekt waar ze voor zijn. Het onderwerp van vandaag sluit aan bij het vorige. Laten we het hebben over abstracte klassen in Java.
Juist vanwege deze 'verwarring', waarbij het object niet weet welk gedrag het moet vertonen, hebben de makers van Java afgezien van meervoudige overerving. U zult zich echter herinneren dat Java-klassen meerdere interfaces kunnen implementeren. Trouwens, tijdens je studie ben je al minstens één abstracte klas tegengekomen!
![Specifieke voorbeelden van abstracte klassen in Java - 1](https://cdn.codegym.cc/images/article/7da9274b-c536-419f-80fb-582c52982717/800.jpeg)
Waarom klassen 'abstract' worden genoemd
U herinnert zich waarschijnlijk wat 'abstractie' is - we hebben het er al over gehad. :) Als je het bent vergeten, vrees dan niet. Onthoud: het is een principe van OOP dat zegt dat we bij het ontwerpen van klassen en het maken van objecten alleen de hoofdeigenschappen van de entiteit moeten identificeren en de minor moeten negeren. Als we bijvoorbeeld eenSchoolTeacher
klasse ontwerpen, hebben we nauwelijks een eigenschap ' height ' nodig. Deze eigenschap is inderdaad niet relevant voor een leraar. Maar als we een BasketballPlayer
klas creëren, dan zou groei een belangrijk kenmerk zijn. Dus luister. Een abstracte klasseis zo abstract als maar kan - een onvoltooide 'blanco' voor een groep toekomstige klassen. De blanco kan niet worden gebruikt zoals deze is. Het is te 'rauw'. Maar het beschrijft een bepaalde toestand en algemeen gedrag dat zal worden bezeten door toekomstige klassen die de abstracte klasse erven.
Voorbeelden van abstracte Java-klassen
Beschouw een eenvoudig voorbeeld met auto's:
public abstract class Car {
private String model;
private String color;
private int maxSpeed;
public abstract void gas();
public abstract void brake();
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
Zo ziet de eenvoudigste abstracte klasse eruit. Zoals je kunt zien, is het niets bijzonders :) Waarom zouden we het nodig hebben? Allereerst beschrijft het onze gewenste entiteit, een auto, op de meest abstracte manier mogelijk. Er is een reden waarom we het woord abstract gebruiken . In de echte wereld zijn er geen 'abstracte auto's'. Er zijn vrachtwagens, raceauto's, sedans, coupés en SUV's. Onze abstracte klasse is gewoon een 'blauwdruk' die we later zullen gebruiken om autoklassen te maken.
public class Sedan extends Car {
@Override
public void gas() {
System.out.println("The sedan is accelerating!");
}
@Override
public void brake() {
System.out.println("The sedan is slowing down!");
}
}
Dit lijkt sterk op waar we het over hadden in de lessen over erfrecht. Maar in die lessen hadden we een autoklasse en de methoden waren niet abstract. Maar die oplossing heeft een aantal nadelen die vastliggen in abstracte klassen. Eerst en vooral kun je geen instantie van een abstracte klasse maken :
public class Main {
public static void main(String[] args) {
Car car = new Car(); // Error! The Car class is abstract!
}
}
De makers van Java hebben deze 'functie' specifiek ontworpen. Nogmaals, ter herinnering: een abstracte klasse is slechts een blauwdruk voor toekomstige 'normale' klassen . Je hebt toch geen kopieën van de blauwdruk nodig? En je maakt geen instanties van een abstracte klasse :) Maar als de Car
klasse niet abstract was, zouden we er gemakkelijk instanties van kunnen maken:
public class Car {
private String model;
private String color;
private int maxSpeed;
public void gas() {
// Some logic
}
public void brake() {
// Some logic
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car(); // Everything is fine. A car is created.
}
}
Nu heeft ons programma een soort onbegrijpelijke auto - het is geen vrachtwagen, geen raceauto, geen sedan, en het is volkomen onduidelijk wat het is. Dit is de 'abstracte auto' die in de natuur niet bestaat. We kunnen hetzelfde voorbeeld geven met behulp van dieren. Stel je voor als Animal
klassen ( abstracte dieren ). Het is onduidelijk wat voor dier het is, tot welke familie het behoort en welke kenmerken het heeft. Het zou vreemd zijn om dat in je programma te zien. Er zijn geen 'abstracte dieren' in de natuur. Alleen honden, katten, vossen, mollen, enz. Abstracte klassen verlossen ons van abstracte objecten. Ze geven ons basistoestand en gedrag. Alle auto's moeten bijvoorbeeld een model , kleur en maximale snelheid hebben en u moet degas en rem . Dat is het. Dit is een algemeen abstract plan. Vervolgens ontwerp je de klassen die je nodig hebt. Opmerking: twee methoden in de klasse abstract worden ook aangeduid als abstract en hebben geen enkele implementatie. De reden is dezelfde: abstracte klassen creëren geen standaardgedrag voor abstracte auto's. Ze geven gewoon aan wat elke auto moet kunnen. Als u echter standaardgedrag nodig heeft, kunt u methoden in een abstracte klasse implementeren. Java verbiedt dit niet:
public abstract class Car {
private String model;
private String color;
private int maxSpeed;
public void gas() {
System.out.println("Gas!");
}
public abstract void brake();
// Getters and setters
}
public class Sedan extends Car {
@Override
public void brake() {
System.out.println("The sedan is slowing down!");
}
}
public class Main {
public static void main(String[] args) {
Sedan sedan = new Sedan();
sedan.gas();
}
}
Console-uitvoer: "Gas!" Zoals je kunt zien, hebben we de eerste methode geïmplementeerd in de abstracte klasse, en niet de tweede. Als resultaat Sedan
is het gedrag van onze klasse verdeeld in twee delen: als je de gas()
methode aanroept, 'stijgt' naar de Car
abstracte ouderklasse, maar we hebben de brake()
methode in de Sedan
klas overschreven. Dit blijkt erg handig en flexibel te zijn. Maar nu is onze klasse niet zo abstract ? De helft van zijn methoden is immers geïmplementeerd. Dit is eigenlijk een heel belangrijk kenmerk - een klasse is abstract als ten minste één van zijn methoden abstract is. Een van de twee methoden, of in ieder geval een van de duizend methoden - het maakt niet uit. We kunnen zelfs alle methoden implementeren en geen ervan abstract laten. Dan zou het een abstracte klasse zijn zonder abstracte methoden. In principe is dit mogelijk, en de compiler zal geen fouten genereren, maar het is beter om dit te vermijden: het woord abstract verliest zijn betekenis, en je collega-programmeurs zullen zeer verrast zijn :/ Tegelijkertijd, als een methode is gemarkeerd met het woord abstract moet elke onderliggende klasse het implementeren of verklaren als abstract. Anders genereert de compiler een fout. Natuurlijk kan elke klasse slechts één abstracte klasse overerven, dus wat betreft overerving is er geen verschil tussen abstracte en gewone klassen. Het maakt niet uit of we een abstracte klasse erven of een gewone, er kan maar één bovenliggende klasse zijn.
Waarom Java geen meerdere overerving van klassen heeft
We hebben al gezegd dat Java geen meervoudige overerving heeft, maar we hebben niet echt onderzocht waarom. Laten we dat nu proberen te doen. Het feit is dat als Java meervoudige overerving zou hebben, onderliggende klassen niet zouden kunnen beslissen welk specifiek gedrag ze zouden moeten kiezen. Stel dat we twee klassen hebben —Toaster
en NuclearBomb
:
public class Toaster {
public void on() {
System.out.println("The toaster is on. Toast is being prepared!");
}
public void off() {
System.out.println("The toaster is off!");
}
}
public class NuclearBomb {
public void on() {
System.out.println("Boom!");
}
}
Zoals je kunt zien, hebben beide een on()
methode. Voor een broodrooster begint het te roosteren. Voor een atoombom veroorzaakt het een explosie. Oeps: / Stel je nu voor dat je hebt besloten (vraag me niet waarom!) om er iets tussenin te maken. En zo heb je een MysteriousDevice
klasse! Deze code werkt natuurlijk niet, en we geven het alleen als voorbeeld 'maar het zou kunnen':
public class MysteriousDevice extends Toaster, NuclearBomb {
public static void main(String[] args) {
MysteriousDevice mysteriousDevice = new MysteriousDevice();
mysteriousDevice.on(); // So what should happen here? Do we get toast or a nuclear apocalypse?
}
}
Laten we eens kijken wat we hebben. Het mysterieuze apparaat erft tegelijkertijd Toaster en NuclearBomb. Beide hebben on()
methoden. Als gevolg hiervan, als we de on()
methode aanroepen, is het onduidelijk welke moet worden aangeroepen op het MysteriousDevice
object. Het object kan het nooit weten. En als klap op de vuurpijl: de NuclearBomb heeft geen off()
methode, dus als we het niet goed hebben geraden, is het onmogelijk om het apparaat uit te schakelen. ![Specifieke voorbeelden van abstracte klassen in Java - 2](https://cdn.codegym.cc/images/article/d6a41f88-49a9-488d-9dc5-8806adcb1ca3/800.jpeg)
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar>
Het is je oude vriend, de Calendar
klas. Het is abstract en heeft meerdere kinderen. Een van hen is GregorianCalendar
. Je gebruikte het al in de lessen over dates. :) Alles lijkt duidelijk genoeg. Er is slechts één vraag: wat is eigenlijk het fundamentele verschil tussen abstracte klassen en interfaces? Waarom hebben ze beide aan Java toegevoegd in plaats van de taal tot één taal te beperken? Dat zou immers ruimschoots voldoende zijn geweest. Daar gaan we het de volgende les over hebben ! Tot dan :)
GO TO FULL VERSION