"Ik ga je vertellen over « toegangsmodificatoren ». Ik heb er al eens eerder over verteld, maar herhaling is een pijler van leren."
U kunt de toegang (zichtbaarheid) die andere klassen hebben tot de methoden en variabelen van uw klasse regelen. Een toegangsmodificator beantwoordt de vraag «Wie heeft toegang tot deze methode/variabele?». U kunt voor elke methode of variabele slechts één modifier specificeren.
1) " publieke " modifier.
Een variabele, methode of klasse gemarkeerd met de public modifier is overal in het programma toegankelijk. Dit is de hoogste graad van openheid: er zijn geen beperkingen.
2) « privé » modifier.
Een variabele, methode of klasse gemarkeerd met de private modifier is alleen toegankelijk in de klasse waarin deze is gedeclareerd. De gemarkeerde methode of variabele is verborgen voor alle andere klassen. Dit is de hoogste graad van privacy: alleen toegankelijk voor jouw klas. Dergelijke methoden worden niet overgeërfd en kunnen niet worden opgeheven. Bovendien zijn ze niet toegankelijk in een afstammelingenklasse.
3) « Standaard modifier».
Als een variabele of methode niet is gemarkeerd met een modifier, wordt deze beschouwd als gemarkeerd met de "standaard" modifier. Variabelen en methoden met deze modifier zijn zichtbaar voor alle klassen in het pakket waarin ze zijn gedeclareerd, en alleen voor die klassen. Deze modifier wordt ook wel " pakket " of " pakket privé " toegang genoemd , waarmee wordt aangegeven dat toegang tot variabelen en methoden open staat voor het hele pakket dat de klasse bevat.
4) « beschermde » modifier.
Dit toegangsniveau is iets ruimer dan pakket . Een variabele, methode of klasse gemarkeerd met de beschermde modifier kan worden benaderd vanuit zijn pakket (zoals "pakket"), en vanuit alle overgeërfde klassen.
Deze tabel legt het allemaal uit:
Soort zichtbaarheid | Trefwoord | Toegang | |||
---|---|---|---|---|---|
Jouw klas | Jouw pakket | Afstammeling | Alle klassen | ||
Privaat | privaat | Ja | Nee | Nee | Nee |
Pakket | (geen modificatie) | Ja | Ja | Nee | Nee |
Beschermd | beschermd | Ja | Ja | Ja | Nee |
Openbaar | openbaar | Ja | Ja | Ja | Ja |
Er is een manier om deze tabel gemakkelijk te onthouden. Stel je voor dat je een testament schrijft. Je verdeelt al je spullen in vier categorieën. Wie mag jouw spullen gebruiken?
Wie heeft toegang | Modificator | Voorbeeld |
---|---|---|
Alleen ik | privaat | Persoonlijk dagboek |
Familie | (geen modificatie) | Familiefoto's |
Familie en erfgenamen | beschermd | Familie landgoed |
Iedereen | openbaar | Memoires |
"Het lijkt veel op het voorstellen dat klassen in hetzelfde pakket deel uitmaken van één familie."
"Ik wil je ook enkele interessante nuances vertellen over overheersende methoden."
1) Impliciete implementatie van een abstracte methode.
Laten we zeggen dat je de volgende code hebt:
class Cat
{
public String getName()
{
return "Oscar";
}
}
En je hebt besloten om een Tiger-klasse te maken die deze klasse overerft, en een interface toe te voegen aan de nieuwe klasse
class Cat
{
public String getName()
{
return "Oscar";
}
}
interface HasName
{
String getName();
int getWeight();
}
class Tiger extends Cat implements HasName
{
public int getWeight()
{
return 115;
}
}
Als u gewoon alle ontbrekende methoden implementeert die IntelliJ IDEA u vertelt te implementeren, zou u later misschien lang moeten zoeken naar een bug.
Het blijkt dat de Tiger-klasse een getName-methode heeft die is geërfd van Cat, die zal worden gebruikt als de implementatie van de getName-methode voor de HasName-interface.
"Daar zie ik niets vreselijks aan."
"Het is niet zo erg, het is een waarschijnlijke plaats voor fouten om binnen te sluipen."
Maar het kan nog erger:
interface HasWeight
{
int getValue();
}
interface HasSize
{
int getValue();
}
class Tiger extends Cat implements HasWeight, HasSize
{
public int getValue()
{
return 115;
}
}
Het blijkt dat je niet altijd kunt erven van meerdere interfaces. Nauwkeuriger gezegd, u kunt ze erven, maar u kunt ze niet correct implementeren. Kijk naar het voorbeeld. Beide interfaces vereisen dat u de methode getValue() implementeert, maar het is niet duidelijk wat deze moet retourneren: het gewicht of de grootte? Dit is heel onaangenaam om mee om te gaan.
"Ik ben het ermee eens. Je wilt een methode implementeren, maar dat lukt niet. Je hebt al een methode geërfd met dezelfde naam van de basisklasse. Die is defect."
"Maar er is goed nieuws."
2) Zichtbaarheid uitbreiden. Wanneer u een type overerft, kunt u de zichtbaarheid van een methode vergroten. Zo ziet het eruit:
Java-code | Beschrijving |
---|---|
|
|
|
We hebben de zichtbaarheid van de methode uitgebreid van protected naar public . |
Code | Waarom dit "legaal" is |
---|---|
|
Alles is geweldig. Hier weten we niet eens dat de zichtbaarheid is uitgebreid in een afstammelingenklasse. |
|
Hier noemen we de methode waarvan de zichtbaarheid is vergroot.
Als dit niet mogelijk was, zouden we altijd een methode in Tiger kunnen declareren: Met andere woorden, we hebben het niet over een beveiligingsschending. |
|
Als aan alle voorwaarden is voldaan die nodig zijn om een methode in een basisklasse ( Cat ) aan te roepen, dan is dat zeker ook het geval voor het aanroepen van de methode op het afstammende type ( Tijger ). Omdat de beperkingen op de methodeaanroep zwak waren, niet sterk. |
"Ik weet niet zeker of ik het helemaal begrepen heb, maar ik zal onthouden dat dit mogelijk is."
3) Vernauwing van het retourtype.
In een overschreven methode kunnen we het retourtype wijzigen in een versmald referentietype.
Java-code | Beschrijving |
---|---|
|
|
|
We hebben de methode overschreven getMyParent en nu retourneert het een Tiger object. |
Code | Waarom dit "legaal" is |
---|---|
|
Alles is geweldig. Hier weten we niet eens dat het retourtype van de getMyParent-methode is verbreed in de afstammingsklasse.
Hoe de «oude code» werkte en werkt. |
|
Hier noemen we de methode waarvan het retourtype is versmald.
Als dit niet mogelijk was, zouden we altijd een methode in Tiger kunnen declareren: Met andere woorden, er zijn geen beveiligingsschendingen en/of type casting-schendingen. |
|
En alles werkt hier prima, hoewel we het type variabelen hebben verbreed naar de basisklasse (Cat).
Vanwege overschrijven wordt de juiste methode setMyParent aangeroepen. En u hoeft zich nergens zorgen over te maken als u de getMyParent-methode aanroept , omdat de geretourneerde waarde, hoewel van de Tiger-klasse, nog steeds probleemloos kan worden toegewezen aan de myParent-variabele van de basisklasse (Cat) . Tiger-objecten kunnen veilig worden opgeslagen in zowel Tiger-variabelen als Cat-variabelen. |
"Ja. Begrepen. Bij het overschrijven van methoden moet je weten hoe dit allemaal werkt als we onze objecten doorgeven aan code die alleen de basisklasse aankan en niets weet over onze klasse. "
"Precies! Dan is de grote vraag waarom we het type van de geretourneerde waarde niet kunnen verfijnen als we een methode overschrijven?"
"Het is duidelijk dat in dit geval de code in de basisklasse niet meer werkt:"
Java-code | Uitleg van het probleem |
---|---|
|
|
|
We hebben de getMyParent-methode overbelast en het type retourwaarde verkleind.
Hier is alles goed. |
|
Dan werkt deze code niet meer.
De methode getMyParent kan elke instantie van een object retourneren, omdat deze in feite wordt aangeroepen voor een Tiger-object. En we hebben geen controle voor de opdracht. Het is dus heel goed mogelijk dat de myParent-variabele van het Cat-type een String-referentie opslaat. |
"Prachtig voorbeeld, Amigo!"
In Java wordt er, voordat een methode wordt aangeroepen, niet gecontroleerd of het object zo'n methode heeft. Alle controles vinden plaats tijdens runtime. En een [hypothetische] aanroep van een ontbrekende methode zou er hoogstwaarschijnlijk toe leiden dat het programma probeert niet-bestaande bytecode uit te voeren. Dit zou uiteindelijk leiden tot een fatale fout en het besturingssysteem zou het programma met geweld sluiten.
"Ho. Nu weet ik het."
GO TO FULL VERSION