"Jag ska berätta om " åtkomstmodifierare ". Jag har berättat om dem en gång tidigare, men upprepning är en pelare för lärande."

Du kan styra åtkomsten (synlighet) som andra klasser har till metoderna och variablerna för din klass. En åtkomstmodifierare svarar på frågan «Vem kan komma åt denna metod/variabel?». Du kan endast ange en modifierare för varje metod eller variabel.

1) " offentlig " modifierare.

En variabel, metod eller klass märkt med public modifier kan nås från var som helst i programmet. Detta är den högsta graden av öppenhet: det finns inga begränsningar.

2) " privat " modifierare.

En variabel, metod eller klass märkt med den privata modifieraren kan endast nås i klassen där den har deklarerats. Den markerade metoden eller variabeln är dold från alla andra klasser. Detta är den högsta graden av integritet: endast tillgänglig för din klass. Sådana metoder ärvs inte och kan inte åsidosättas. Dessutom kan de inte nås i en nedstigande klass.

3)  « Standardmodifierare ».

Om en variabel eller metod inte är markerad med någon modifierare, anses den vara markerad med "default" modifieraren. Variabler och metoder med denna modifierare är synliga för alla klasser i paketet där de deklareras, och endast för dessa klasser. Denna modifierare kallas också " paket " eller " paket privat " åtkomst, vilket antyder att åtkomst till variabler och metoder är öppen för hela paketet som innehåller klassen.

4) " skyddad " modifierare.

Denna åtkomstnivå är något bredare än paketet . En variabel, metod eller klass märkt med den skyddade modifieraren kan nås från dess paket (som "paket") och från alla ärvda klasser.

Den här tabellen förklarar allt:

Typ av synlighet Nyckelord Tillgång
Din klass Ditt paket Descendent Alla klasser
Privat privat Ja Nej Nej Nej
Paket (ingen modifierare) Ja Ja Nej Nej
Skyddad skyddad Ja Ja Ja Nej
offentlig offentlig Ja Ja Ja Ja

Det finns ett sätt att enkelt komma ihåg den här tabellen. Föreställ dig att du skriver ett testamente. Du delar upp alla dina saker i fyra kategorier. Vem får använda dina saker?

Vem har tillgång Modifierare Exempel
Bara  jag privat Personlig dagbok
Familj (ingen modifierare) Familjefoton
Familj och arvingar skyddad Familjegods
Alla offentlig Memoarer

"Det är ungefär som att föreställa sig att klasser i samma paket är en del av en familj."

"Jag vill också berätta några intressanta nyanser om överordnade metoder."

1) Implicit implementering av en abstrakt metod.

Låt oss säga att du har följande kod:

Koda
class Cat
{
 public String getName()
 {
  return "Oscar";
 }
}

Och du bestämde dig för att skapa en Tiger-klass som ärver den här klassen och lägga till ett gränssnitt till den nya klassen

Koda
class Cat
{
 public String getName()
 {
   return "Oscar";
 }
}
interface HasName
{
 String getName();
 int getWeight();
}
class Tiger extends Cat implements HasName
{
 public int getWeight()
 {
  return 115;
 }

}

Om du bara implementerar alla de saknade metoderna som IntelliJ IDEA säger åt dig att implementera, kan du senare lägga ner lång tid på att leta efter en bugg.

Det visar sig att klassen Tiger har en getName-metod som är ärvd från Cat, som kommer att tas som implementering av getName-metoden för HasName-gränssnittet.

"Jag ser inget hemskt med det."

"Det är inte så illa, det är en trolig plats för misstag att smyga sig på."

Men det kan vara ännu värre:

Koda
interface HasWeight
{
 int getValue();
}
interface HasSize
{
 int getValue();
}
class Tiger extends Cat implements HasWeight, HasSize
{
 public int getValue()
 {
  return 115;
 }
}

Det visar sig att du inte alltid kan ärva från flera gränssnitt. Mer exakt, du kan ärva dem, men du kan inte implementera dem korrekt. Titta på exemplet. Båda gränssnitten kräver att du implementerar getValue()-metoden, men det är inte klart vad den ska returnera: vikten eller storleken? Detta är ganska obehagligt att ha att göra med.

"Jag håller med. Du vill implementera en metod, men du kan inte. Du har redan ärvt en metod med samma namn från basklassen. Den är trasig."

"Men det finns goda nyheter."

2) Utökad synlighet. När du ärver en typ kan du utöka synligheten för en metod. Så här ser det ut:

Java-kod Beskrivning
class Cat
{
 protected String getName()
 {
  return "Oscar";
 }
}
class Tiger extends Cat
{
 public String getName()
 {
  return "Oscar Tiggerman";
 }
}
Vi har utökat metodens synlighet från protectedtill public.
Koda Varför detta är "lagligt"
public static void main(String[] args)
{
 Cat cat = new Cat();
 cat.getName();
}
Allt är bra. Här vet vi inte ens att sikten har utökats i en ättlingklass.
public static void main(String[] args)
{
 Tiger tiger = new Tiger();
 tiger.getName();
}
Här kallar vi metoden vars synlighet har utökats.

Om detta inte var möjligt kunde vi alltid deklarera en metod i Tiger:
public String getPublicName()
{
super.getName(); //kalla den skyddade metoden
}

Med andra ord, vi pratar inte om något säkerhetsbrott.

public static void main(String[] args)
{
 Cat catTiger = new Tiger();
 catTiger.getName();
}
Om alla villkor som är nödvändiga för att anropa en metod i en basklass ( Cat ) är uppfyllda, är de säkert uppfyllda för att anropa metoden på den härstammande typen ( Tiger ). Eftersom begränsningarna för metodanropet var svaga, inte starka.

"Jag är inte säker på att jag förstod helt, men jag ska komma ihåg att det här är möjligt."

3) Begränsning av returtypen.

I en åsidosatt metod kan vi ändra returtypen till en avsmalnande referenstyp.

Java-kod Beskrivning
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Tiger getMyParent()
 {
  return (Tiger) this.parent;
 }
}
Vi åsidosatte metoden getMyParentoch nu returnerar den ett Tigerobjekt.
Koda Varför detta är "lagligt"
public static void main(String[] args)
{
 Cat parent = new Cat();

 Cat me = new Cat();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
Allt är bra. Här vet vi inte ens att getMyParent-metodens returtyp har breddats i descendant-klassen.

Hur den "gamla koden" fungerade och fungerar.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Tiger me = new Tiger();
 me.setMyParent(parent);
 Tiger myParent = me.getMyParent();
}
Här kallar vi metoden vars returtyp har minskats.

Om detta inte var möjligt kunde vi alltid deklarera en metod i Tiger:
public Tiger getMyTigerParent()
{
return (Tiger) this.parent;
}

Det finns med andra ord inga säkerhetsöverträdelser och/eller överträdelser av typen casting.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
Och allt fungerar bra här, även om vi breddade variablernas typ till basklassen (Cat).

På grund av överstyrning anropas den korrekta setMyParent-metoden.

Och det finns inget att oroa sig för när man anropar getMyParent-metoden , eftersom returvärdet, även om det är Tiger-klassen, fortfarande kan tilldelas myParent-variabeln för basklassen (Cat) utan problem.

Tigerobjekt kan säkert lagras både i Tiger-variabler och Cat-variabler.

"Japp. Jag förstår. När man åsidosätter metoder måste man vara medveten om hur allt detta fungerar om vi skickar våra objekt till kod som bara kan hantera basklassen och inte vet något om vår klass. "

"Precis! Då är den stora frågan varför vi inte kan begränsa returvärdets typ när vi åsidosätter en metod?"

"Det är uppenbart att i det här fallet skulle koden i basklassen sluta fungera:"

Java-kod Förklaring av problemet
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Object getMyParent()
 {
  if (this.parent != null)
   return this.parent;
  else
   return "I'm an orphan";
 }
}
Vi överbelastade getMyParent-metoden och minskade typen av dess returvärde.

Allt är bra här.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 Cat myParent = me.getMyParent();
}
slutar den här koden att fungera.

GetMyParent-metoden kan returnera vilken instans som helst av ett objekt, eftersom det faktiskt anropas på ett Tiger-objekt.

Och vi har ingen kontroll innan uppdraget. Således är det fullt möjligt att Cat-type myParent-variabeln kommer att lagra en String-referens.

"Underbart exempel, Amigo!"

I Java, innan en metod anropas, finns det ingen kontroll om objektet har en sådan metod. Alla kontroller sker vid körning. Och ett [hypotetiskt] anrop till en saknad metod skulle med största sannolikhet få programmet att försöka exekvera en bytekod som inte finns. Detta skulle i slutändan leda till ett fatalt fel och operativsystemet skulle tvångsstänga programmet.

"Wow. Nu vet jag."