"Jeg skal fortelle deg om « adgangsmodifikatorer ». Jeg fortalte om dem en gang før, men repetisjon er en pilar for læring."
Du kan kontrollere tilgangen (synligheten) som andre klasser har til metodene og variablene til klassen din. En tilgangsmodifikator svarer på spørsmålet «Hvem kan få tilgang til denne metoden/variabelen?». Du kan spesifisere bare én modifikator for hver metode eller variabel.
1) « offentlig » modifikator.
En variabel, metode eller klasse merket med den offentlige modifikatoren kan nås fra hvor som helst i programmet. Dette er den høyeste graden av åpenhet: det er ingen begrensninger.
2) « privat » modifikator.
En variabel, metode eller klasse merket med den private modifikatoren kan bare nås i klassen der den er deklarert. Den merkede metoden eller variabelen er skjult fra alle andre klasser. Dette er den høyeste grad av personvern: kun tilgjengelig for klassen din. Slike metoder er ikke arvet og kan ikke overstyres. I tillegg kan de ikke nås i en etterkommerklasse.
3) « Standard modifikator».
Hvis en variabel eller metode ikke er merket med noen modifikator, anses den for å være merket med "standard" modifikator. Variabler og metoder med denne modifikatoren er synlige for alle klasser i pakken der de er deklarert, og bare for disse klassene. Denne modifikatoren kalles også " pakke " eller " pakke privat " tilgang, og antyder at tilgang til variabler og metoder er åpen for hele pakken som inneholder klassen.
4) « beskyttet » modifikator.
Dette tilgangsnivået er litt bredere enn pakken . En variabel, metode eller klasse merket med den beskyttede modifikatoren kan nås fra pakken (som "pakke") og fra alle arvede klasser.
Denne tabellen forklarer det hele:
Type synlighet | Nøkkelord | Adgang | |||
---|---|---|---|---|---|
Din klasse | Pakken din | Descendent | Alle klasser | ||
Privat | privat | Ja | Nei | Nei | Nei |
Pakke | (ingen modifikator) | Ja | Ja | Nei | Nei |
Beskyttet | beskyttet | Ja | Ja | Ja | Nei |
Offentlig | offentlig | Ja | Ja | Ja | Ja |
Det er en måte å enkelt huske denne tabellen. Tenk deg at du skriver et testamente. Du deler alle tingene dine inn i fire kategorier. Hvem får bruke tingene dine?
Hvem har tilgang | Modifikator | Eksempel |
---|---|---|
Bare meg | privat | Personlig dagbok |
Familie | (ingen modifikator) | Familiebilder |
Familie og arvinger | beskyttet | Familie eiendom |
Alle | offentlig | Memoarer |
"Det er mye som å forestille seg at klasser i samme pakke er en del av en familie."
"Jeg vil også fortelle deg noen interessante nyanser om overordnede metoder."
1) Implisitt implementering av en abstrakt metode.
La oss si at du har følgende kode:
class Cat
{
public String getName()
{
return "Oscar";
}
}
Og du bestemte deg for å lage en Tiger-klasse som arver denne klassen, og legge til et grensesnitt til den nye klassen
class Cat
{
public String getName()
{
return "Oscar";
}
}
interface HasName
{
String getName();
int getWeight();
}
class Tiger extends Cat implements HasName
{
public int getWeight()
{
return 115;
}
}
Hvis du bare implementerer alle de manglende metodene som IntelliJ IDEA ber deg implementere, kan du senere ende opp med å bruke lang tid på å søke etter en feil.
Det viser seg at Tiger-klassen har en getName-metode arvet fra Cat, som vil bli tatt som implementering av getName-metoden for HasName-grensesnittet.
— Jeg ser ikke noe forferdelig ved det.
"Det er ikke så ille, det er et sannsynlig sted for feil å snike seg inn."
Men det kan bli enda verre:
interface HasWeight
{
int getValue();
}
interface HasSize
{
int getValue();
}
class Tiger extends Cat implements HasWeight, HasSize
{
public int getValue()
{
return 115;
}
}
Det viser seg at du ikke alltid kan arve fra flere grensesnitt. Mer presist kan du arve dem, men du kan ikke implementere dem riktig. Se på eksempelet. Begge grensesnittene krever at du implementerer getValue()-metoden, men det er ikke klart hva den skal returnere: vekten eller størrelsen? Dette er ganske ubehagelig å måtte forholde seg til.
"Jeg er enig. Du vil implementere en metode, men du kan ikke. Du har allerede arvet en metode med samme navn fra basisklassen. Den er ødelagt."
"Men det er gode nyheter."
2) Utvide synlighet. Når du arver en type, kan du utvide synligheten til en metode. Slik ser det ut:
Java-kode | Beskrivelse |
---|---|
|
|
|
Vi har utvidet metodens synlighet fra protected til public . |
Kode | Hvorfor er dette «lovlig» |
---|---|
|
Alt er flott. Her vet vi ikke engang at sikten er utvidet i en etterkommerklasse. |
|
Her kaller vi metoden hvis synlighet er utvidet.
Hvis dette ikke var mulig, kunne vi alltid erklære en metode i Tiger: Med andre ord, vi snakker ikke om noen sikkerhetsbrudd. |
|
Hvis alle betingelsene som er nødvendige for å kalle en metode i en basisklasse ( Cat ) er oppfylt , er de absolutt tilfredsstilt for å kalle metoden på den etterkommere typen ( Tiger ) . Fordi restriksjonene på metodekallet var svake, ikke sterke. |
— Jeg er ikke sikker på at jeg forsto det helt, men jeg skal huske at dette er mulig.
3) Innsnevring av returtypen.
I en overstyrt metode kan vi endre returtypen til en innsnevret referansetype.
Java-kode | Beskrivelse |
---|---|
|
|
|
Vi overstyrte metoden getMyParent , og nå returnerer den et Tiger objekt. |
Kode | Hvorfor er dette «lovlig» |
---|---|
|
Alt er flott. Her vet vi ikke engang at getMyParent-metodens returtype har blitt utvidet i etterkommerklassen.
Hvordan den «gamle koden» fungerte og fungerer. |
|
Her kaller vi metoden hvis returtype er blitt innsnevret.
Hvis dette ikke var mulig, kunne vi alltid erklære en metode i Tiger: Det er med andre ord ingen sikkerhetsbrudd og/eller type casting brudd. |
|
Og alt fungerer bra her, selv om vi utvidet variablenes type til basisklassen (Cat).
På grunn av overstyring kalles den riktige setMyParent-metoden. Og det er ingenting å bekymre seg for når du kaller getMyParent-metoden , fordi returverdien, selv om Tiger-klassen, fortsatt kan tildeles til myParent-variabelen til basisklassen (Cat) uten problemer. Tiger-objekter kan trygt lagres både i Tiger-variabler og Cat-variabler. |
"Jepp. Skjønner det. Når du overstyrer metoder, må du være klar over hvordan alt dette fungerer hvis vi sender objektene våre til kode som bare kan håndtere basisklassen og ikke vet noe om klassen vår. "
"Akkurat! Da er det store spørsmålet hvorfor vi ikke kan begrense returverdiens type når vi overstyrer en metode?"
"Det er åpenbart at i dette tilfellet vil koden i basisklassen slutte å virke:"
Java-kode | Forklaring av problemet |
---|---|
|
|
|
Vi overbelastet getMyParent-metoden og begrenset typen returverdi.
Alt er bra her. |
|
Da slutter denne koden å fungere.
GetMyParent-metoden kan returnere enhver forekomst av et objekt, fordi det faktisk kalles på et Tiger-objekt. Og vi har ikke sjekk før oppdraget. Dermed er det fullt mulig at Cat-type myParent-variabelen vil lagre en strengreferanse. |
"Fantastisk eksempel, Amigo!"
I Java, før en metode kalles, er det ingen sjekk om objektet har en slik metode. Alle kontroller skjer ved kjøretid. Og et [hypotetisk] kall til en manglende metode vil mest sannsynlig føre til at programmet forsøker å kjøre en ikke-eksisterende bytekode. Dette ville til slutt føre til en fatal feil, og operativsystemet ville tvangslukke programmet.
"Wow. Nå vet jeg det."