CodeGym /Java Blog /Willekeurig /Geneste binnenklassen
John Squirrels
Niveau 41
San Francisco

Geneste binnenklassen

Gepubliceerd in de groep Willekeurig
Hoi! Vandaag gaan we in op een belangrijk onderwerp: hoe geneste klassen werken in Java. Met Java kun je klassen binnen een andere klasse maken:

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
Deze interne klassen worden genest genoemd. Ze zijn onderverdeeld in 2 soorten:
  1. Niet-statische geneste klassen. Dit worden ook wel innerlijke klassen genoemd.
  2. Statische geneste klassen.
Innerlijke klassen hebben op hun beurt twee verschillende subcategorieën. Behalve dat een inner class gewoon een inner class is, kan het ook zijn:
  • een lokale klas
  • een anonieme klas
Verward? :) Dat is goed. Hier is een diagram voor de duidelijkheid. Kom er tijdens de les op terug als je ineens in de war raakt! Geneste binnenklassen - 2In de les van vandaag bespreken we interne klassen (ook wel niet-statische geneste klassen genoemd). Ze zijn speciaal gemarkeerd in het algemene diagram, zodat u niet verdwaalt :) Laten we beginnen met de voor de hand liggende vraag: waarom worden ze "innerlijke" klassen genoemd? Het antwoord is vrij eenvoudig: omdat ze in andere klassen worden gemaakt. Hier is een voorbeeld:

public class Bicycle {

   private String model;
   private int weight;

   public Bicycle(String model, int weight) {
       this.model = model;
       this.weight = weight;
   }
  
   public void start() {
       System.out.println("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {
      
       public void up() {

           System.out.println("Seat up!");
       }
      
       public void down() {

           System.out.println("Seat down!");
       }
   }
}
Hier hebben we de Bicycleklas. Het heeft 2 velden en 1 methode: start(). Geneste binnenklassen - 3Het verschilt van een gewone klasse doordat het twee klassen bevat: Handlebaren Seat. Hun code is in de klas geschreven Bicycle. Dit zijn volwaardige klassen: zoals u kunt zien, heeft elk van hen zijn eigen methoden. Op dit punt heb je misschien een vraag: waarom zouden we in vredesnaam de ene klas in de andere plaatsen? Waarom zouden ze innerlijke klassen maken? Stel dat we aparte lessen nodig hebben voor de concepten stuur en zadel in ons programma. Het is natuurlijk niet nodig dat we ze genest maken! We kunnen gewone klassen maken. Bijvoorbeeld als volgt:

public class Handlebar {
   public void right() {
       System.out.println("Steer right!");
   }

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
Zeer goede vraag! Natuurlijk worden we niet beperkt door de technologie. Dat doen is zeker een optie. Hier gaat het meer om het juiste ontwerp van de klassen vanuit het perspectief van een specifiek programma en het doel ervan. Innerlijke klassen zijn voor het scheiden van een entiteit die onlosmakelijk verbonden is met een andere entiteit. Stuur, zadels en pedalen zijn onderdelen van een fiets. Los van de fiets hebben ze weinig zin. Als we van al deze concepten afzonderlijke openbare klassen zouden maken, zouden we de volgende code in ons programma hebben gehad:

public class Main {

   public static void main(String[] args) {
       Handlebar handlebar = new Handlebar();
       handlebar.right();
   }
}
Hmm... De betekenis van deze code is zelfs moeilijk uit te leggen. We hebben een vaag stuur (waarom is het nodig? Geen idee, om eerlijk te zijn). En deze hendel draait naar rechts... helemaal vanzelf, zonder fiets... om de een of andere reden. Door het concept van het stuur te scheiden van het concept van de fiets, verloren we enige logica in ons programma. Met behulp van een innerlijke klasse ziet de code er heel anders uit:

public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.Handlebar handlebar = peugeot.new Handlebar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handlebar.left();
       handlebar.right();
   }
}
Console-uitvoer:

Seat up! 
Let's go! 
Steer left! 
Steer right!
Nu is wat we zien ineens logisch! :) We hebben een fietsobject gemaakt. We hebben twee "subobjecten" voor fietsen gemaakt: een stuur en een stoel. We verhoogden de stoel voor comfort en daar gingen we: trappen en sturen als dat nodig was! :) De methoden die we nodig hebben, worden aangeroepen op de juiste objecten. Het is allemaal eenvoudig en handig. In dit voorbeeld verbetert het scheiden van het stuur en het zadel de inkapseling (we verbergen gegevens over de fietsonderdelen binnen de relevante klasse) en stelt ons in staat een meer gedetailleerde abstractie te creëren. Laten we nu eens naar een andere situatie kijken. Stel dat we een programma willen maken dat een fietsenwinkel en reserveonderdelen voor fietsen simuleert. Geneste binnenklassen - 4In deze situatie zal onze vorige oplossing niet werken. In een fietsenwinkel is elk afzonderlijk fietsonderdeel zinvol, ook los van een fiets. We hebben bijvoorbeeld methoden nodig zoals "pedalen verkopen aan een klant", "een nieuw zadel kopen", enz. Het zou een vergissing zijn om hier interne klassen te gebruiken - elk afzonderlijk fietsonderdeel in ons nieuwe programma heeft een betekenis die op zijn eigen: het kan worden gescheiden van het concept van een fiets. Dit is precies waar u op moet letten als u zich afvraagt ​​of u interne klassen moet gebruiken of alle entiteiten als afzonderlijke klassen moet organiseren. Objectgeoriënteerd programmeren is goed omdat het het gemakkelijk maakt om echte entiteiten te modelleren. Dit kan uw leidraad zijn bij het beslissen of u innerlijke klassen wilt gebruiken. In een echte winkel reserveonderdelen zijn gescheiden van fietsen - dit is oké. Dit betekent dat het ook goed is bij het ontwerpen van een programma. Oké, we hebben de "filosofie" bedacht :) Laten we nu eens kennis maken met belangrijke "technische" kenmerken van innerlijke klassen. Dit is wat u absoluut moet onthouden en begrijpen:
  1. Een object van een binnenklasse kan niet bestaan ​​zonder een object van een buitenklasse.

    Dit is logisch: daarom hebben we de Seaten Handlebarinnerlijke klassen in ons programma opgenomen - zodat we niet eindigen met verweesde sturen en stoelen.

    Deze code compileert niet:

    
    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }
    

    Hieruit volgt nog een belangrijk kenmerk:

  2. Een object van een binnenklasse heeft toegang tot de variabelen van de buitenklasse.

    Laten we bijvoorbeeld een int seatPostDiametervariabele (die de diameter van de zadelpen vertegenwoordigt) aan onze Bicycleklas toevoegen.

    Vervolgens Seatkunnen we in de innerlijke klasse een displaySeatProperties()methode maken die de stoeleigenschappen weergeeft:

    
    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    

    En nu kunnen we deze informatie weergeven in ons programma:

    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.displaySeatProperties();
       }
    }
    

    Console-uitvoer:

    
    Seat properties: seatpost diameter = 40
    

    Opmerking:de nieuwe variabele wordt gedeclareerd met de strengste toegangsmodificator ( private). En toch heeft de binnenklas toegang!

  3. Een object van een binnenklasse kan niet worden gemaakt in een statische methode van een buitenklasse.

    Dit wordt verklaard door de specifieke kenmerken van hoe innerlijke klassen zijn georganiseerd. Een binnenklasse kan constructors met parameters hebben, of alleen de standaardconstructor. Maar hoe dan ook, wanneer we een object van een binnenste klasse creëren, wordt een verwijzing naar het object van de buitenste klasse onzichtbaar doorgegeven aan het gecreëerde object van de binnenste klasse. De aanwezigheid van zo'n objectreferentie is immers een absolute vereiste. Anders kunnen we geen objecten van de innerlijke klasse maken.

    Maar als een methode van de buitenste klasse statisch is, dan hebben we misschien geen object van de buitenste klasse! En dit zou een schending zijn van de logica van hoe een innerlijke klasse werkt. In deze situatie genereert de compiler een fout:

    
    public static Seat createSeat() {
      
       // Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
    
  4. Een binnenklasse kan geen statische variabelen en methoden bevatten.

    De logica is hetzelfde: statische methoden en variabelen kunnen bestaan ​​en kunnen worden aangeroepen of waarnaar wordt verwezen, zelfs als er geen object is.

    Maar zonder een object van de buitenste klasse hebben we geen toegang tot de binnenste klasse.

    Een duidelijke tegenstelling! Dit is de reden waarom statische variabelen en methoden niet zijn toegestaan ​​in innerlijke klassen.

    De compiler genereert een foutmelding als u ze probeert te maken:

    
    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
          
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    
  5. Bij het maken van een object van een innerlijke klasse is de toegangsmodificator belangrijk.

    Een binnenklasse kan worden gemarkeerd met de standaard toegangsmodificaties: public, private, protected, en package private.

    Waarom is dit van belang?

    Dit beïnvloedt waar we instanties van de innerlijke klasse in ons programma kunnen maken.

    Als onze Seatklasse is gedeclareerd als public, kunnen we Seatobjecten in elke andere klasse maken. De enige vereiste is dat er ook een object van de buitenste klasse moet bestaan.

    Dat hebben we hier trouwens al gedaan:

    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.Handlebar handlebar = peugeot.new Handlebar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handlebar.left();
           handlebar.right();
       }
    }
    

    We kregen gemakkelijk toegang tot de Handlebarbinnenste klas van de Mainklas.

    Als we de innerlijke klasse declareren als private, kunnen we alleen objecten maken binnen de buitenste klasse.

    SeatWe kunnen niet langer een object "aan de buitenkant" maken :

    
    private class Seat {
    
       // Methods
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           // Bicycle.Seat has private access in Bicycle
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }
    

    Je begrijpt de logica waarschijnlijk al :)

  6. Toegangsmodificatoren voor interne klassen werken hetzelfde als voor gewone variabelen.

    De protectedmodifier geeft toegang tot een instantievariabele in subklassen en klassen die zich in hetzelfde pakket bevinden.

    protectedwerkt ook voor innerlijke klassen. We kunnen protectedobjecten van de innerlijke klasse maken:

    • in de buitenste klasse;
    • in zijn subklassen;
    • in lessen die in hetzelfde pakket zitten.

    Als de binnenklasse geen toegangsmodificator ( package private) heeft, kunnen objecten van de binnenklasse worden gemaakt:

    • in de buitenste klasse;
    • in lessen die in hetzelfde pakket zitten.

    U bent al lang bekend met modifiers, dus geen problemen hier.

Dat was alles voor nu :) Maar verslap niet! Innerlijke klassen zijn een vrij uitgebreid onderwerp dat we in de volgende les zullen blijven onderzoeken. Nu kun je je geheugen opfrissen van de les van onze cursus over innerlijke lessen . En laten we het de volgende keer hebben over statische geneste klassen.
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION