Wat is het belangrijkste verschil tussen een innerlijke klasse en een statische geneste klasse in Java? Speelt ontwerp / implementatie een rol bij het kiezen van een van deze?
Wat is het belangrijkste verschil tussen een innerlijke klasse en een statische geneste klasse in Java? Speelt ontwerp / implementatie een rol bij het kiezen van een van deze?
Van de Java-zelfstudie:
Geneste klassen zijn onderverdeeld in twee categorieën: statisch en niet-statisch. Geneste klassen die statisch zijn verklaard, worden gewoon statische geneste klassen genoemd. Niet-statische geneste klassen worden innerlijke klassen genoemd.
Statische geneste klassen worden benaderd met behulp van de insluitende klassenaam:
OuterClass.StaticNestedClass
Als u bijvoorbeeld een object voor de statische geneste klasse wilt maken, gebruikt u deze syntaxis:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
Objecten die instanties zijn van een innerlijke klasse, bestaan binnen een instantie van de buitenklasse. Beschouw de volgende klassen:
class OuterClass {
...
class InnerClass {
...
}
}
Een instantie van InnerClass kan alleen bestaan binnen een instantie van OuterClass en heeft directe toegang tot de methoden en velden van de bijbehorende instantie.
Als u een innerlijke klasse wilt instantiëren, moet u eerst de buitenklasse instellen. Maak vervolgens het binnenste object binnen het buitenste object met deze syntaxis:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
zien: Java-zelfstudie - geneste klassen
Voor de volledigheid merk op dat er ook zoiets bestaat als een innerlijke klasse zonder een insluitende instantie:
class A {
int t() { return 1; }
static A a = new A() { int t() { return 2; } };
}
Hier, new A() { ... }
is een innerlijke klasse gedefinieerd in een statische context en heeft geen insluitende instantie.
Terminologie: Geneste klassen zijn verdeeld in twee categorieën: statisch en niet-statisch. Geneste klassen dat worden verklaard statisch worden gewoon genoemd statische geneste klassen. Niet-statische geneste klassen worden innerlijk genoemd klassen.
In het spraakgebruik worden de termen 'genest' en 'innerlijk' door de meeste programmeurs door elkaar gebruikt, maar ik gebruik de juiste term 'geneste klasse' die zowel innerlijk als statisch weergeeft.
Klassen kunnen worden genest ad infinitumb.v. klasse A kan klasse B bevatten die klasse C bevat die klasse D, enz. bevat. Echter, meer dan één niveau van klassennesten is zeldzaam, omdat het over het algemeen slecht ontwerp is.
Er zijn drie redenen waarom u een geneste klasse zou kunnen maken:
Er zijn vier soorten geneste klasse in Java. Kort gezegd zijn ze:
Ik zal nader ingaan op meer details.
Statische klassen zijn het gemakkelijkst te begrijpen, omdat ze niets te maken hebben met instanties van de klasse containing.
Een statische klasse is een klasse die is gedeclareerd als een statisch lid van een andere klasse. Net als andere statische leden, is zo'n klasse eigenlijk slechts een hanger waarop de bevattende klasse als naamruimte wordt gebruikt, bijv. de klas Geit verklaard als een statisch lid van de klas Neushoorn in het pakket pizza staat bekend onder de naam pizza.Rhino.Goat.
package pizza;
public class Rhino {
...
public static class Goat {
...
}
}
Eerlijk gezegd, statische klassen zijn een behoorlijk waardeloze functie omdat klassen al door pakketten zijn verdeeld in naamruimten. De enige echt denkbare reden om een statische klasse te maken is dat een dergelijke klasse toegang heeft tot de privé-statische leden van zijn bevattende klasse, maar ik vind dit een vrij zwakke rechtvaardiging voor het bestaan van de statische klassefunctie.
Een innerlijke klasse is een klasse die is gedeclareerd als een niet-statisch lid van een andere klasse:
package pizza;
public class Rhino {
public class Goat {
...
}
private void jerry() {
Goat g = new Goat();
}
}
Net als bij een statische klasse staat de binnenklasse bekend als gekwalificeerd door de klassenklasse, pizza.Rhino.Goat, maar binnen de klasse die het bevat, kan het bekend zijn door zijn eenvoudige naam. Elk exemplaar van een innerlijke klasse is echter gekoppeld aan een specifieke instantie van de klasse waarin het is gehuisvest: hierboven, de Geit gemaakt in Jerry, is impliciet gebonden aan de Neushoorn aanleg deze in Jerry. Anders maken we de bijbehorende Neushoorn bijvoorbeeld expliciet wanneer we instantiëren Geit:
Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();
(Merk op dat je naar het innerlijke type verwijst als rechtvaardig Geit in het rare nieuwe syntaxis: Java leidt het omvattende type uit de neushoorn een deel. En ja nieuwe rhino.Goat () zou ook voor mij logischer zijn geweest.)
Dus wat levert dit ons op? Welnu, de instantie van de inner class heeft toegang tot de instantie-leden van de instantie van de klasse die aanwezig is. Deze insluitende instantieleden worden binnen de innerlijke klasse aangeduid via gewoon hun eenvoudige namen, niet via deze (deze in de binnenste klasse verwijst naar de instantie inner class, niet de bijbehorende instantie van de klasse containing):
public class Rhino {
private String barry;
public class Goat {
public void colin() {
System.out.println(barry);
}
}
}
In de innerlijke klasse kun je verwijzen naar deze van de bevattende klasse als Rhino.this, en je kunt gebruiken deze om naar zijn leden te verwijzen, bijv. Rhino.this.barry.
Een lokale innerlijke klasse is een klasse die wordt gedeclareerd in de hoofdtekst van een methode. Een dergelijke klasse is alleen bekend binnen de methode die deze bevat, zodat deze alleen kan worden geïnstantieerd en de leden ervan binnen de bijbehorende methode kunnen worden benaderd. De winst is dat een instantie van de lokale inner klasse gebonden is aan en toegang heeft tot de laatste lokale variabelen van zijn bevattende methode. Wanneer de instantie een laatste lokaal van de bijbehorende methode gebruikt, behoudt de variabele de waarde die hij had op het moment van het maken van de instantie, zelfs als de variabele buiten het bereik is gegaan (dit is in feite de ruwe, beperkte versie van sluitingen van Java).
Omdat een lokale innerlijke klasse niet het lid van een klasse of pakket is, wordt deze niet aangegeven met een toegangsniveau. (Houd er echter rekening mee dat de eigen leden toegangsniveaus hebben zoals in een normale klas.)
Als een lokale innerlijke klasse wordt gedeclareerd in een instantiemethode, wordt een instantiatie van de innerlijke klasse gekoppeld aan de instantie die wordt vastgehouden door de methode die de methode bevat deze op het moment van het maken van de instantie, en dus zijn de instantieleden van de bevattende klasse toegankelijk zoals in een instantie binnenklasse. Een lokale innerlijke klasse wordt eenvoudig geïnstantieerd via Zijn naam, bijv. lokale innerlijke klasse Kat wordt geïnstantieerd als nieuwe kat (), niet nieuw this.Cat () zoals je zou verwachten.
Een anonieme innerlijke klasse is een syntactisch handige manier om een lokale innerlijke klasse te schrijven. Meestal wordt een lokale innerlijke klasse maximaal één keer geïnstantieerd telkens wanneer de bijbehorende methode wordt uitgevoerd. Het zou dan leuk zijn als we de definitie van de lokale innerlijke klasse en de afzonderlijke instantiatie zouden kunnen combineren in één handige syntaxisvorm, en het zou ook leuk zijn als we geen naam voor de klas hoefden bedenken (hoe minder onbehulpzaam namen die je code bevat, des te beter). Een anonieme innerlijke klasse laat beide dingen toe:
new *ParentClassName*(*constructorArgs*) {*members*}
Dit is een expressie die een nieuw exemplaar retourneert van een naamloze klasse die zich uitbreidt ParentClassName. Je kunt niet je eigen constructor leveren; integendeel, men wordt impliciet geleverd die eenvoudigweg de superconstructor aanroept, dus de geleverde argumenten moeten in de superconstructor passen. (Als de ouder meerdere constructors bevat, wordt de "eenvoudigste" "eenvoudigste" genoemd, zoals bepaald door een nogal ingewikkelde set regels die niet de moeite waard zijn om gedetailleerd te leren - let gewoon op wat NetBeans of Eclipse je vertellen.)
Als alternatief kunt u een interface opgeven om te implementeren:
new *InterfaceName*() {*members*}
Zo'n verklaring creëert een nieuw exemplaar van een naamloze klasse die Object en implementaties uitbreidt Interfacenaam. Nogmaals, u kunt niet uw eigen constructor leveren; in dit geval levert Java impliciet een constructor van no-arg, do-nothing (dus in dit geval zullen er nooit constructorargumenten zijn).
Hoewel je een anonieme innerlijke klasse geen constructor kunt geven, kun je nog steeds elke gewenste setup uitvoeren met behulp van een initialisatieblok (een {} blok dat buiten elke methode wordt geplaatst).
Wees duidelijk dat een anonieme innerlijke klasse eenvoudigweg een minder flexibele manier is om met één instantie een lokale innerlijke klasse te creëren. Als u een lokale innerlijke klasse wilt die meerdere interfaces implementeert of interfaces implementeert terwijl u een andere klasse uitbreidt dan Voorwerp of die zijn eigen constructor specificeert, je bent vast aan het creëren van een reguliere benoemde lokale innerlijke klasse.
Ik denk niet dat het echte verschil in de bovenstaande antwoorden duidelijk werd.
Eerst om de voorwaarden goed te krijgen:
Martin's antwoord heeft tot nu toe gelijk. De eigenlijke vraag is echter: wat is het doel van het declareren van een geneste klasse statisch of niet?
Je gebruikt statische geneste klassen als je gewoon je klassen bij elkaar wilt houden als ze lokaal bij elkaar horen of als de geneste klasse uitsluitend wordt gebruikt in de klasse omsluiten. Er is geen semantisch verschil tussen een statische geneste klasse en elke andere klasse.
Niet-statische geneste klassen zijn een ander beest. Net als anonieme innerlijke klassen, zijn dergelijke geneste klassen eigenlijk sluitingen. Dat betekent dat ze hun omringende bereik en hun omhullende instantie vastleggen en dat toegankelijk maken. Misschien zal een voorbeeld dat verduidelijken. Zie dit streepje van een container:
public class Container {
public class Item{
Object data;
public Container getContainer(){
return Container.this;
}
public Item(Object data) {
super();
this.data = data;
}
}
public static Item create(Object data){
// does not compile since no instance of Container is available
return new Item(data);
}
public Item createSubItem(Object data){
// compiles, since 'this' Container is available
return new Item(data);
}
}
In dit geval wilt u een verwijzing van een onderliggende artikel naar de bovenliggende container. Met een niet-statische geneste klasse werkt dit zonder enig werk. U kunt het insluitende exemplaar van Container openen met de syntaxis Container.this
.
Meer hardcore uitleg volgt:
Als u de Java-bytecodes bekijkt die de compiler genereert voor een (niet-statische) geneste klasse, wordt deze mogelijk nog duidelijker:
// class version 49.0 (49)
// access flags 33
public class Container$Item {
// compiled from: Container.java
// access flags 1
public INNERCLASS Container$Item Container Item
// access flags 0
Object data
// access flags 4112
final Container this$0
// access flags 1
public getContainer() : Container
L0
LINENUMBER 7 L0
ALOAD 0: this
GETFIELD Container$Item.this$0 : Container
ARETURN
L1
LOCALVARIABLE this Container$Item L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 1
public <init>(Container,Object) : void
L0
LINENUMBER 12 L0
ALOAD 0: this
ALOAD 1
PUTFIELD Container$Item.this$0 : Container
L1
LINENUMBER 10 L1
ALOAD 0: this
INVOKESPECIAL Object.<init>() : void
L2
LINENUMBER 11 L2
ALOAD 0: this
ALOAD 2: data
PUTFIELD Container$Item.data : Object
RETURN
L3
LOCALVARIABLE this Container$Item L0 L3 0
LOCALVARIABLE data Object L0 L3 2
MAXSTACK = 2
MAXLOCALS = 3
}
Zoals u kunt zien, maakt de compiler een verborgen veld Container this$0
. Dit wordt ingesteld in de constructor die een extra parameter van het type Container heeft om de insluitende instantie op te geven. U kunt deze parameter niet in de bron zien, maar de compiler genereert deze impliciet voor een geneste klasse.
Martin's voorbeeld
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
zou zo worden gecompileerd tot een oproep van zoiets (in bytecodes)
new InnerClass(outerObject)
Voor de volledigheid:
Een anonieme les is een perfect voorbeeld van een niet-statische geneste klasse waaraan gewoon geen naam is gekoppeld en waaraan later niet kan worden gerefereerd.
Ik denk dat geen van de bovenstaande antwoorden u het echte verschil uitleggen tussen een geneste klasse en een statische geneste klasse in termen van toepassingsontwerp:
Een geneste klasse kan niet-statisch of statisch zijn en in elk geval is een klasse die is gedefinieerd in een andere klasse. Een geneste klasse moet alleen bestaan om te dienen om klasse bij te sluiten, als een geneste klasse nuttig is voor andere klassen (niet alleen de insluitingen), moet deze worden gedeclareerd als een klasse op het hoogste niveau.
Niet-statische geneste klasse : is impliciet geassocieerd met de omsluitende instantie van de bevattende klasse, dit betekent dat het mogelijk is om methoden en toegangsvariabelen van de omsluitende instantie op te roepen. Een veelgebruikt gebruik van een niet-statische geneste klasse is het definiëren van een adapterklasse.
Statische geneste klasse : kan geen toegang krijgen tot insluitende klasseninstantie en invoke-methoden erop, dus moet worden gebruikt wanneer de geneste klasse geen toegang vereist tot een instantie van de insluitende klasse. Een veelgebruikt gebruik van statische geneste klasse is het implementeren van een component van het buitenste object.
Dus het belangrijkste verschil tussen de twee vanuit een ontwerpstandpunt is: niet-gestapelde geneste klasse heeft toegang tot de instantie van de containerklasse, terwijl statische niet kan.
In eenvoudige termen hebben we geneste klassen nodig, voornamelijk omdat Java geen sluitingen biedt.
Geneste klassen zijn klassen die zijn gedefinieerd in de hoofdtekst van een andere insluitende klasse. Ze zijn van twee soorten - statisch en niet-statisch.
Ze worden behandeld als leden van de omsluitende klasse, vandaar dat u een van de vier toegangsspecificaties kunt specificeren - private, package, protected, public
. We hebben deze luxe niet bij klassen van het hoogste niveau, die alleen kunnen worden uitgeroepen public
of pakket-privé.
Inner classes aka Non-stack klassen hebben toegang tot andere leden van de topklasse, zelfs als ze privé worden verklaard, terwijl statische geneste klassen geen toegang hebben tot andere leden van de topklasse.
public class OuterClass {
public static class Inner1 {
}
public class Inner2 {
}
}
Inner1
is onze statische innerlijke klasse en Inner2
is onze innerlijke klasse die niet statisch is. Het belangrijkste verschil tussen beide is dat je geen kunt maken Inner2
bijvoorbeeld zonder een Outer waar je een kunt maken Inner1
object onafhankelijk.
Wanneer zou je Innerlijke klasse gebruiken?
Denk aan een situatie waarin Class A
en Class B
zijn verwant, Class B
moet toegang hebben Class A
leden, en Class B
is alleen gerelateerd aan Class A
. Innerlijke klassen komen in beeld.
Voor het maken van een instantie van de innerlijke klasse, moet je een instantie van je uiterlijke klasse maken.
OuterClass outer = new OuterClass();
OuterClass.Inner2 inner = outer.new Inner2();
of
OuterClass.Inner2 inner = new OuterClass().new Inner2();
Wanneer zou u de statische Innerklasse gebruiken?
U zou een statische binnenklasse definiëren als u weet dat deze geen relatie heeft met de instantie van de omsluitende klasse / topklasse. Als je innerlijke klasse geen methoden of velden van de uiterlijke klasse gebruikt, is het alleen maar verspilling van ruimte, dus maak het statisch.
Als u bijvoorbeeld een object voor de statische geneste klasse wilt maken, gebruikt u deze syntaxis:
OuterClass.Inner1 nestedObject = new OuterClass.Inner1();
Het voordeel van een statische geneste klasse is dat deze geen object van de klasse containing / topklasse nodig heeft om te werken. Dit kan u helpen om het aantal objecten dat uw toepassing tijdens runtime maakt te verminderen.
Ik denk dat de conventie die over het algemeen wordt gevolgd, deze is:
Er zijn echter weinig andere punten om te onthouden zijn:
Klassen op het hoogste niveau en statische geneste klassen zijn semantisch hetzelfde, behalve dat in geval van statische geneste klasse het statische verwijzing kan maken naar private statische velden / methoden van de buitenste [bovenliggende] klasse en vice versa.
Binnenklassen hebben toegang tot instantievariabelen van het insluitende exemplaar van de klasse Outer [boven]. Niet alle binnenklassen hebben echter insluitende instanties, bijvoorbeeld innerlijke klassen in statische contexten, zoals een anonieme klasse die wordt gebruikt in een statisch initialisatieblok, niet.
De anonieme klasse standaard breidt de bovenliggende klasse uit of implementeert de bovenliggende interface en er is geen verdere clausule om een andere klasse uit te breiden of meer interfaces te implementeren. Zo,
new YourClass(){};
middelen class [Anonymous] extends YourClass {}
new YourInterface(){};
middelen class [Anonymous] implements YourInterface {}
Ik voel dat de grotere vraag die openblijft welke moet worden gebruikt en wanneer? Nou dat hangt meestal af van het scenario waarmee je te maken hebt, maar het lezen van het antwoord van @jrudolph kan je helpen een beslissing te nemen.
Hier zijn de belangrijkste verschillen en overeenkomsten tussen de Java-binnenklasse en de statische geneste klasse.
Hoop dat het helpt!
Geassocieerd met instantie van omsluitende klasse dus om te instantiëren heeft het eerst een instantie van de buitenklasse nodig (noot nieuwe trefwoord plaats):
Outerclass.InnerClass innerObject = outerObject.new Innerclass();
Kan niet definieer een statische leden zelf
Geen toegang buitenste klasse aanleg methoden of velden
Niet geassocieerd met een instantie van klasse omsluiten Dus om het te tonen:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
Volgens de documentatie van Oracle zijn er verschillende redenen (volledige documentatie):
Het is een manier om lesgroepen logisch te groeperen die slechts op één plaats worden gebruikt: Als een klasse nuttig is voor slechts één andere klasse, is het logisch om de klasse in die klasse in te bedden en de twee bij elkaar te houden. Door dergelijke "helperklassen" te nesten, wordt hun pakket gestroomlijnder.
Het verhoogt de inkapseling: Overweeg twee klassen op het hoogste niveau, A en B, waarbij B toegang nodig heeft tot leden van A die anders privé zouden zijn verklaard. Door klasse B te verbergen in klasse A, kunnen A-leden privé worden verklaard en kan B toegang krijgen. Bovendien kan B zelf worden verborgen voor de buitenwereld.
Het kan leiden tot meer leesbare en onderhoudbare code: Door kleine klassen in klassen van het hoogste niveau te nestelen, wordt de code dichter bij de locatie geplaatst.
Geneste klasse: klasse in de klas
Soorten:
Verschil:
Niet-statische geneste klasse [Innerlijke klasse]
In niet-statisch genest klasseobject van innerlijke klasse bestaan binnen het object van de buitenklasse. Zodat het datalid van de uiterlijke klasse toegankelijk is voor de innerlijke klasse. Dus om een object van innerlijke klasse te creëren, moeten we eerst een object van de uiterlijke klasse maken.
outerclass outerobject=new outerobject();
outerclass.innerclass innerobjcet=outerobject.new innerclass();
Statische geneste klasse
In statisch genest klasseobject van binnenklasse geen object van de buitenklasse nodig, omdat het woord "statisch" aangeeft dat er geen object hoeft te worden gemaakt.
class outerclass A {
static class nestedclass B {
static int x = 10;
}
}
Als u toegang wilt tot x, schrijf dan de volgende inside-methode
outerclass.nestedclass.x; i.e. System.out.prinltn( outerclass.nestedclass.x);
Het exemplaar van de binnenklasse wordt gemaakt wanneer een instantie van de buitenklasse wordt gemaakt. Daarom hebben de leden en methoden van de binnenste klasse toegang tot de leden en methoden van de instantie (object) van de buitenklasse. Wanneer de instantie van de buitenklasse buiten bereik raakt, houden ook de voorbeelden uit de innerlijke klasse op te bestaan.
De statische geneste klasse heeft geen concrete instantie. Het wordt gewoon geladen wanneer het voor de eerste keer wordt gebruikt (net als de statische methoden). Het is een volledig onafhankelijke entiteit, waarvan de methoden en variabelen geen toegang hebben tot de instanties van de buitenklasse.
De statisch geneste klassen zijn niet gekoppeld aan het buitenobject, ze zijn sneller en nemen geen Heap / Stack-geheugen aan, omdat het niet nodig is om een instantie van een dergelijke klasse te maken. Daarom is de vuistregel om te proberen om statische geneste klasse te definiëren, met een zo beperkt mogelijk bereik (privé> = klasse> = beschermd> = openbaar), en het vervolgens om te zetten naar de binnenklasse (door de "statische" identifier te verwijderen) en los te maken het bereik, als het echt nodig is.
Er is een subtiliteit over het gebruik van geneste statische klassen die in bepaalde situaties nuttig kunnen zijn.
Terwijl statische attributen worden geïnstantieerd voordat de klasse wordt geïnstantieerd via zijn constructor, Statische kenmerken in geneste statische klassen lijken niet te worden geïnstantieerd tot na de de constructor van de klasse wordt aangeroepen, of ten minste niet tot nadat eerst naar de attributen is verwezen, zelfs als ze zijn gemarkeerd als 'definitief'.
Beschouw dit voorbeeld:
public class C0 {
static C0 instance = null;
// Uncomment the following line and a null pointer exception will be
// generated before anything gets printed.
//public static final String outerItem = instance.makeString(98.6);
public C0() {
instance = this;
}
public String makeString(int i) {
return ((new Integer(i)).toString());
}
public String makeString(double d) {
return ((new Double(d)).toString());
}
public static final class nested {
public static final String innerItem = instance.makeString(42);
}
static public void main(String[] argv) {
System.out.println("start");
// Comment out this line and a null pointer exception will be
// generated after "start" prints and before the following
// try/catch block even gets entered.
new C0();
try {
System.out.println("retrieve item: " + nested.innerItem);
}
catch (Exception e) {
System.out.println("failed to retrieve item: " + e.toString());
}
System.out.println("finish");
}
}
Hoewel 'genest' en 'innerItItIt' beide worden gedeclareerd als 'statische finale'. de instelling van nested.innerItem vindt niet plaats tot nadat de klasse is geïnstantieerd (of tenminste pas nadat er eerst naar het genestelde statische item is verwezen), zoals u zelf kunt zien door commentaar te geven en de opmerkingen ongedaan te maken waar ik naar verwijs, hierboven. Hetzelfde geldt niet waar voor 'outerItem'.
Dit is tenminste wat ik zie in Java 6.0.