Gids voor overerving in Java

1. Overzicht

Een van de kernprincipes van objectgeoriënteerd programmeren - overerving - stelt ons in staat om bestaande code te hergebruiken of een bestaand type uit te breiden.

Simpel gezegd, in Java kan een klasse een andere klasse en meerdere interfaces erven, terwijl een interface andere interfaces kan erven.

In dit artikel beginnen we met de noodzaak van overerving, en gaan we verder met hoe overerving werkt met klassen en interfaces.

Vervolgens bespreken we hoe de namen van variabelen / methoden en toegangsmodificatoren de leden beïnvloeden die worden overgeërfd.

En aan het einde zullen we zien wat het betekent om een ​​type te erven.

2. De noodzaak van overerving

Stel je voor dat je als autofabrikant meerdere automodellen aanbiedt aan je klanten. Hoewel verschillende automodellen verschillende functies kunnen bieden, zoals een schuifdak of kogelvrije ramen, zouden ze allemaal gemeenschappelijke componenten en functies bevatten, zoals motor en wielen.

Het is logisch om maak een basisontwerp en breid het uit om hun gespecialiseerde versies te maken, in plaats van elk automodel afzonderlijk vanaf nul te ontwerpen.

Op een vergelijkbare manier kunnen we met overerving een klasse maken met basisfuncties en gedrag en de gespecialiseerde versies ervan maken door klassen te maken die deze basisklasse erven. Op dezelfde manier kunnen interfaces bestaande interfaces uitbreiden.

We zullen opmerken dat er meerdere termen worden gebruikt om te verwijzen naar een type dat wordt geërfd door een ander type, met name:

  • een basistype wordt ook wel een super- of oudertype genoemd
  • een afgeleid type wordt een extended, sub of child type genoemd

3. Klasse-overerving

3.1. Een klas uitbreiden

Een klasse kan een andere klasse erven en extra leden definiëren.

Laten we beginnen met het definiëren van een basisklasse Auto:

openbare klasse Auto {int wielen; String model; void start () {// Controleer essentiële onderdelen}}

De klas Gepantserde auto kan de leden erven van Auto klasse door met behulp van het trefwoord strekt zich uit in zijn verklaring:

openbare klasse ArmoredCar breidt Car uit {int bulletProofWindows; void remoteStartCar () {// dit voertuig kan worden gestart met een afstandsbediening}}

We kunnen nu zeggen dat de Gepantserde auto class is een subklasse van Auto, en de laatste is een superklasse van Gepantserde auto.

Klassen in Java ondersteunen enkelvoudige overerving; de Gepantserde auto class kan niet meerdere klassen uitbreiden.

Merk ook op dat bij afwezigheid van een strekt zich uit trefwoord, erft een klasse impliciet klasse java.lang.Object.

Een subklasseklasse erft het niet-statische beschermd en openbaar leden uit de superklasse. Daarnaast zijn de leden met standaard en pakket toegang wordt overgenomen als de twee klassen zich in hetzelfde pakket bevinden.

Aan de andere kant is het privaat en statisch leden van een klas worden niet geërfd.

3.2. Toegang tot ouderleden vanuit een kindklasse

Om toegang te krijgen tot overgeërfde eigenschappen of methoden, kunnen we ze eenvoudig rechtstreeks gebruiken:

openbare klasse ArmoredCar breidt Car uit {public String registerModel () {retourmodel; }}

Merk op dat we geen verwijzing naar de superklasse nodig hebben om toegang te krijgen tot zijn leden.

4. Overerving van interface

4.1. Meerdere interfaces implementeren

Hoewel klassen slechts één klasse kunnen erven, kunnen ze meerdere interfaces implementeren.

Stel je de Gepantserde auto die we in het vorige gedeelte hebben gedefinieerd, is vereist voor een superspion. Dus de Auto productiebedrijf dacht aan het toevoegen van vliegende en zwevende functionaliteit:

openbare interface Floatable {leegte floatOnWater (); }
openbare interface Flyable {void fly (); }
openbare klasse ArmoredCar breidt uit Auto implementeert Floatable, Flyable {public void floatOnWater () {System.out.println ("I can float!"); } public void fly () {System.out.println ("Ik kan vliegen!"); }}

In bovenstaand voorbeeld merken we het gebruik van het trefwoord op werktuigen erven van een interface.

4.2. Problemen met meervoudige overerving

Java maakt meervoudige overerving mogelijk met behulp van interfaces.

Tot Java 7 was dit geen probleem. Interfaces konden alleen maar definiëren abstract methoden, dat wil zeggen methoden zonder enige implementatie. Dus als een klasse meerdere interfaces implementeerde met dezelfde methodehandtekening, was dat geen probleem. De uitvoerende klasse had uiteindelijk maar één methode om te implementeren.

Laten we eens kijken hoe deze eenvoudige vergelijking is veranderd met de introductie van standaard methoden in interfaces, met Java 8.

Beginnend met Java 8, konden interfaces ervoor kiezen om standaardimplementaties voor zijn methoden te definiëren (een interface kan nog steeds definiëren abstract methoden). Dit betekent dat als een klasse meerdere interfaces implementeert, die methoden met dezelfde handtekening definiëren, de onderliggende klasse afzonderlijke implementaties zou erven. Dit klinkt ingewikkeld en is niet toegestaan.

Java staat de overerving van meerdere implementaties van dezelfde methoden, gedefinieerd in afzonderlijke interfaces, niet toe.

Hier is een voorbeeld:

openbare interface Floatable {standaard ongeldige reparatie () {System.out.println ("Reparatie van zwevend object"); }}
openbare interface Flyable {default void repair () {System.out.println ("Flyable object repareren"); }}
openbare klasse ArmoredCar breidt Car implementeert Floatable, Flyable {// dit kan niet compileren}

Als we beide interfaces willen implementeren, moeten we de reparatie() methode.

Als de interfaces in de voorgaande voorbeelden variabelen definiëren met dezelfde naam, bijvoorbeeld looptijd, we kunnen er geen toegang toe krijgen zonder de naam van de variabele vooraf te laten gaan met de interfacenaam:

openbare interface Floatable {int duration = 10; }
openbare interface Flyable {int duration = 20; }
openbare klasse ArmoredCar breidt uit Auto implementeert Floatable, Flyable {public void aMethod () {System.out.println (duur); // zal System.out.println (Floatable.duration) niet compileren; // outputs 10 System.out.println (Flyable.duration); // uitgangen 20}}

4.3. Interfaces die andere interfaces uitbreiden

Een interface kan meerdere interfaces uitbreiden. Hier is een voorbeeld:

openbare interface Floatable {leegte floatOnWater (); }
interface-interface Flyable {void fly (); }
openbare interface SpaceTraveller breidt Floatable, Flyable {void remoteControl (); }

Een interface erft andere interfaces door het trefwoord te gebruiken strekt zich uit. Klassen gebruiken het trefwoord werktuigen om een ​​interface te erven.

5. Overervingstype

Wanneer een klasse een andere klasse of interfaces erft, erft ze niet alleen hun leden, maar ook hun type. Dit geldt ook voor een interface die andere interfaces erft.

Dit is een zeer krachtig concept, waarmee ontwikkelaars dat kunnen programma naar een interface (basisklasse of interface), in plaats van te programmeren voor hun implementaties.

Stel u bijvoorbeeld een toestand voor waarbij een organisatie een lijst bijhoudt van de auto's die eigendom zijn van haar werknemers. Natuurlijk kunnen alle medewerkers verschillende automodellen bezitten. Dus hoe kunnen we verwijzen naar verschillende auto-instanties? Hier is de oplossing:

openbare klasse Werknemer {privé Stringnaam; privé auto auto; // standaard constructor}

Omdat alle afgeleide klassen van Auto erven het type Auto, kan naar de afgeleide klasse-instanties worden verwezen met behulp van de variabele class Auto:

Werknemer e1 = nieuwe werknemer ("Shreya", nieuwe ArmoredCar ()); Medewerker e2 = nieuwe medewerker ("Paul", nieuwe SpaceCar ()); Medewerker e3 = nieuwe medewerker ("Pavni", nieuwe BMW ());

6. Verborgen klasleden

6.1. Hidden Instance-leden

Wat gebeurt er als zowel de superklasse als de subklasse definiëren een variabele of methode met dezelfde naam? Maak je geen zorgen; we hebben nog steeds toegang tot beide. We moeten echter onze intentie duidelijk maken aan Java door de sleutelwoorden voor de variabele of methode te plaatsen dit of super.

De dit trefwoord verwijst naar de instantie waarin het wordt gebruikt. De super trefwoord (zoals het voor de hand lijkt te liggen) verwijst naar de instantie van de bovenliggende klasse:

openbare klasse ArmoredCar breidt Car {private String-model uit; public String getAValue () {retourneer super.model; // retourneert de waarde van het model gedefinieerd in de basisklasse Auto // retourneert dit.model; // retourneert de waarde van het model dat is gedefinieerd in ArmoredCar // retourneert model; // retourneert de waarde van het model dat is gedefinieerd in ArmoredCar}}

Veel ontwikkelaars gebruiken dit en super trefwoorden om expliciet aan te geven naar welke variabele of methode ze verwijzen. Als u ze echter met alle leden gebruikt, kan onze code er rommelig uitzien.

6.2. Verborgen statische leden

Wat gebeurt er wanneer onze basisklasse en subklassen statische variabelen en methoden met dezelfde naam definiëren? Kunnen we toegang krijgen tot een statisch lid uit de basisklasse, in de afgeleide klasse, zoals we dat doen voor de instantievariabelen?

Laten we het aan de hand van een voorbeeld bekijken:

public class Car {public static String msg () {return "Car"; }}
public class ArmoredCar breidt Car uit {public static String msg () {return super.msg (); // dit zal niet compileren. }}

Nee, dat kunnen we niet. De statische leden behoren tot een klasse en niet tot instanties. We kunnen de niet-statische dus niet gebruiken super trefwoord in msg ().

Omdat statische leden tot een klasse behoren, kunnen we de voorgaande aanroep als volgt wijzigen:

retourneer Car.msg ();

Beschouw het volgende voorbeeld, waarin zowel de basisklasse als de afgeleide klasse een statische methode definiëren msg () met dezelfde handtekening:

public class Car {public static String msg () {return "Car"; }}
public class ArmoredCar breidt Car uit {public static String msg () {return "ArmoredCar"; }}

Hier is hoe we ze kunnen noemen:

Auto eerst = nieuwe ArmoredCar (); ArmoredCar second = nieuwe ArmoredCar ();

Voor de voorgaande code, first.msg () zal “Car en second.msg () zal "ArmoredCar" weergeven. Het statische bericht dat wordt aangeroepen, is afhankelijk van het type variabele waarnaar wordt verwezen Gepantserde auto voorbeeld.

7. Conclusie

In dit artikel hebben we een kernaspect van de Java-taal behandeld: overerving.

We hebben gezien hoe Java enkele overerving ondersteunt met klassen en meervoudige overerving met interfaces en bespraken de fijne kneepjes van hoe het mechanisme werkt in de taal.

Zoals altijd is de volledige broncode voor de voorbeelden beschikbaar op GitHub.