PDA

View Full Version : [JAVA] Istanziare un oggetto di una classe interna


caralu
27-09-2006, 10:43
Ciao a tutti! Ho un problema:
sto creando 2 classi (BonusVita e BonusVelocità) interne ad una classe madre Bonus. Vorrei istanziare dall'esterno (dalla classe Game) degli oggetti di tipo BonusVita e BonusVelocità,non posso farlo?
Ho pensato anche di creare come classi esterne le 2 classi BonusVita e BonusVelocità facendogli estendere a entrambe la classe Bonus (da cui devono ereditare tutti i metodi e le variabili), ma non mi sembra la soluzione ottimale (sarebbe più facile da implementare ma vorrei fare la cosa più "lineare" a livello progettuale), dato che queste classi risulterebbero piccolissime, con solo un metodo. Potete darmi qualche consiglio?

thebol
27-09-2006, 10:58
Ciao a tutti! Ho un problema:
sto creando 2 classi (BonusVita e BonusVelocità) interne ad una classe madre Bonus. Vorrei istanziare dall'esterno (dalla classe Game) degli oggetti di tipo BonusVita e BonusVelocità,non posso farlo?
Ho pensato anche di creare come classi esterne le 2 classi BonusVita e BonusVelocità facendogli estendere a entrambe la classe Bonus (da cui devono ereditare tutti i metodi e le variabili), ma non mi sembra la soluzione ottimale (sarebbe più facile da implementare ma vorrei fare la cosa più "lineare" a livello progettuale), dato che queste classi risulterebbero piccolissime, con solo un metodo. Potete darmi qualche consiglio?

factory method?
in bonus fai un createBonusVita e createBonusVelocita, entrambti statici che creano la classe.

ps.e buona norma di solito fare una classe per .java e usare il meno possibile classi interne, cmq fai tu :)
il fatto che siano piccole non è un problema

andbin
27-09-2006, 10:58
Ciao a tutti! Ho un problema:
sto creando 2 classi (BonusVita e BonusVelocità) interne ad una classe madre Bonus. Vorrei istanziare dall'esterno (dalla classe Game) degli oggetti di tipo BonusVita e BonusVelocità,non posso farlo?Certo che puoi farlo, ma hai bisogno di una istanza della classe che contiene le inner class (Bonus).

Quindi:
Bonus b = new Bonus (); // istanza di Bonus

...

Bonus.BonusVita vita = b.new BonusVita ();
Bonus.BonusVelocita velocita = b.new BonusVelocita ();

PGI-Bis
27-09-2006, 11:03
Se la classe Y è interna ad X ma non annidata la sintassi è:

X.Y y = x.new Y();

con x riferimento ad un'istanza di X.

Se la classe Y è annidata in X la sintassi è:

X.Y y = new X.Y();

Una classe interna (inner) è annidata (nested) se è dichiarata static.

PGI-Bis
27-09-2006, 11:05
Stavo scrivendo quando ha risposto Andbin... ma un bel semaforone che dica "guarda, o' grullo d'un pgi, che qualcuno ha già risposto mentre tu stai qui a cincischiare" non farebbe comodo? :D HTTP fa schifo...

PGI-Bis
27-09-2006, 12:17
Per la parte "progettuale" la questione top-level, inner o nested non è difficile da spiegare ma neppure breve. Se hai il tempo di leggere una risposta-papiro posso scriverla.

thebol
27-09-2006, 14:35
Per la parte "progettuale" la questione top-level, inner o nested non è difficile da spiegare ma neppure breve. Se hai il tempo di leggere una risposta-papiro posso scriverla.
fai pure, non fa mai male leggere qualcosa su questi argomenti:)

porfax
27-09-2006, 16:41
Secondo me ti conviene di + creare tutte classi separate.... sarà che ho una visione di java molto OO

PGI-Bis
27-09-2006, 23:26
:D Sarò breve e circostanziato. :D

Poco dopo la scomparsa dei dinosauri...

A me preme sempre l'esigenza di spiegare l'utilità degli strumenti offerti da un linguaggio ai fini della produzione di un sistema orientato agli oggetti e non, talvolta capita di sentirlo, di spiegare l'orientamento agli oggetti attraverso gli strumenti offerti da un linguaggio. Credo quindi che occorra vedere cosa facciano quegli strumenti, secondo le loro regole, e poi cercare di capire se essi occupino o non occupino una qualche casella della prospettiva orientata agli oggetti. Insomma: è Java che mira ad essere orientato agli oggetti e non l'orientamento agli oggetti ad essere Java (o C++ o Python e persino Smalltalk).

Io penso che l'orientamento agli oggetti possa essere spiegato con l'ausilio di due soli termini: oggetto e relazione.

Oggetto è qualsiasi cosa di cui sia predicabile l'identità esclusiva con sè stesso. La relazione è un predicato riferibile ad uno o più oggetti. L'unicità della relazione (un certo predicato e non un altro) rende la relazione stessa un oggetto.

Una classe Java è il modello a partire dal quale sono prodotti dei valori tipizzati detti istanze. Tutte le istanze prodotte da un medesimo modello sono accomunate dal possesso di un insieme strutturalmente identico di elementi (membri, enfaticamente detti "di istanza" perchè ciò che non è "di istanza" non è membro).

Una classe Java appare quindi idonea a rappresentare il concetto di relazione. Una classe Java è la relazione che intercorre tra tutti gli oggetti che ne siano istanza.

Una bizzarria: secondo la teoria delll'orientamento agli oggetti su descritta una classe Java, in quanto relazione, è anche un oggetto. Ma non andate a dirlo in giro :D. Si badi bene che l'asserto non è valido dal punto di vista del linguaggio di programmazione Java: una classe Java, nel senso del modello, non è un oggetto Java.

Poichè ogni relazione è un oggetto e ogni relazione è un predicato riferibile ad uno o più oggetti, nulla vieta che esista una relazione tra relazioni ed una relazione tra un oggetto ed una relazione.

Detto questo e premesso che in Java una classe interna è una classe annidata, non esplicitamente o implicitamente dichiarata static, quali sono le caratteristiche di una classe Java interna?

La sua dichiarazione interviene all'interno del corpo di un'altra classe:

class TopLevel {
class Inner() {}
}

Per effetto del contenimento, gli enunciati presenti nel corpo della classe Inner hanno accesso ai membri, ai costruttori, ai metodi statici ed ai tipi statici dichiarati in TopLevel a prescindere dal modificatore di visibilità che intervenga nella loro dichiarazione.

class TopLevel {
private int x;

private TopLevel() {}

class Inner() {
Inner() {
TopLevel t = new TopLevel();
t.x = 20;
x = 40;
}
}
}

Per creare istanze della classe interna Inner è necessario disporre di un riferimento di tipo TopLevel (meglio, di un tipo compatibile in assegnamento con TopLevel che erediti il membro Inner da un punto in cui le norme sull'accesso rendano operabile il tipo TopLevel.Inner)

Ogni istanza della classe interna è associata ad un'istanza della classe che la contiene (quella usata all'atto della creazione).

public class TopLevel {

public class Inner() {
TopLevel getOwner() {
return TopLevel.this;
}
}
}

TopLevel top = new TopLevel();
TopLevel.Inner inner = top.new Inner();
//top == inner.getOwner()

Questo è lo strumento "tipo interno Java".

Come ogni classe Java la classe interna è una relazione tra tutte le sue istanze. Sotto questo profilo è quindi adatta a rappresentare l'insieme di caratteristiche che accomunano degli elementi in un sistema di riferimento.

Ma per rappresentare una relazione tout-court, in Java, c'è già lo strumento classe top-level che è anche più semplice da usare.

Se lo scopo è quindi quello di dichiarare delle categorie di oggetti – BonusVista e BonusVelocità – allora lo strumento Java apparentemente più idoneo è la classe top-level.

Ogni istanza di Inner è collegata ad un'istanza di TopLevel. Ogni inner è funzionalmente collegato ad un outer. Più inner possono essere collegati allo stesso outer.

Astrattamente è una caratteristica molto (ma molto, ma mi viene da dire "ohhh com'è molto") interessante. Nel sistema di riferimento tra uno o più BonusVita e un Bonus e tra uno o più BonusVelocità e un Bonus esiste qualche collegamento funzionale? Capita che un BonusVelocità deva fare qualcosa sempre sullo stesso Bonus e che più BonusVelocità vogliano farlo sullo stesso Bonus?

Se sì allora la classe Java interna è uno strumento intimamente idoneo a rappresentare quella relazione. Personalmente non vedo una relazione di questo genere tra BonusVita e Bonus o tra BonusVelocità e Bonus ma non posso escludere che effettivamente vi sia.

E' rilevante la dipendenza "semantica" della classe interna dalla classe che la contiene? Per intenderci, il fatto che un inner non sia semplicemente un Inner ma abbia il segno TopLevel.Inner? Be', è senz'altro un predicato comune a più oggetti. Dunque se scrivo:

public class Macchina {
public class Motore {}
}


dico che esiste una relazione tra Macchina e Motore nominalmente espressa in Java con Macchina.Motore, tipo delle istanze di Motore. Tuttavia se la relazione che si vuole esprimere è interamente rappresentabile con la sola dipendenza nominale, la parte relativa al collegamento tra le istanze è un significato che ci si porta dietro anche se non ce ne importa nulla.

public class Aeroplano {
public class Mela {

}
}

Qui suona come andare dal barbiere e dire "barba e capelli: le mele oggi costano due euro al chilo". Parte del messaggio – il collegamento funzionale tra le istanze di Mela e un'istanza di Aeroplano – è inutile (almeno secondo il significato umano dei termini). E non si può fare a meno di dirlo. Vedremo più avanti come questo non sia vero per le classi annidate.

Conclusione sulle classi interne (rullo di tamburi!):

Ogni classe Java interna è una relazione univoca tra una relazione ed un oggetto (funzionale) e una relazione biunivoca tra relazioni (nominale).

Classi annidate (non interne).

E' una classe dichiarata static all'interno di un'altra classe o interfaccia (le interfacce annidate non sono mai interne).

public class TopLevel {

public static class Nested {

}
}

Nel corpo di Nested non esiste un riferimento TopLevel.this: le istanze di Nested sono slegate dalle istanze di TopLevel.

Per creare un'istanza di Nested non è necessario disporre di un riferimento TopLevel.

TopLevel.Nested nested = new TopLevel.Nested();

I membri accessibili in TopLevel sono accessibili anche in Nested a prescindere dal modificatore di accesso. Poichè non esiste un riferimento TopLevel.this e poichè un membro è accessibile solo attraverso un riferimento compatibile in assegnamento con il tipo che possiede il membro acceduto (nel rispetto delle norme sull'ereditabilità e accessibilità), affinchè in Nested si possa "far qualcosa" con i membri di TopLevel occorre che Nested riceva o crei un riferimento di tipo TopLevel. Insomma, se passi a un Nested un TopLevel smanaccia anche sui membri private :D.

Nested è una classe Java.

La domanda è la stessa che ci siamo fatti per le classi inner: date le caratteristiche della classe annidata risulta utile all'espressione di relazioni o oggetti?

Come per la classe interna, anche la classe annidata è idonea a rappresentare una relazione generica. Abbiamo però visto che è più conveniente rappresentare questo fatto con una classe TopLevel. Tanto per essere chiari in questi due casi:

public class Pippo {

}

public class TopLevel {
public static class Pippo {

}
}

Tanto Pippo quanto TopLevel.Pippo sono relazioni tra oggetti ma, se questa è l'esigenza, Pippo appare decisamente più conveniente di TopLevel.Pippo.

Manca il collegamento molto molto interessante tra l'istanza della classe TopLevel e le istanze di Nested che avevamo ravvisato nel caso TopLevel-Inner.

C'è ancora quella relazione "nominale": un Nested non è semplicemente tale ma è un TopLevel.Nested. E qui la cosa si fa interessante (soprattutto per caralu). A differenza del caso delle classi interne una classe annidata esprime una relazione nominale "pura". Solo "barba e capelli", per tornare alla metafora del barbiere.

La relazione nominale pura è il tipo più semplice di relazione che si può ravvisare in un sistema quando lo si osservi con gli occhi dell'orientamento agli oggetti. "Quelle due cose lì hanno un che di comune. Cosa? Non lo so ma qualcosa ce l'hanno". Ci spari una relazione nominale :D. Tutte le relazioni sono nominali ma, in Java, solo la relazione che intercorre tra una classe annidata e la classe continente è "esclusivamente nominale". Be' a dire il vero c'è anche il package ma se dico che il package è una relazione poi sembro strano (è una relazione, tiè :D).

Conclusione.

Una classe Java annidata esprime una relazione tra relazioni (nominale tra la classe annidata e la classe continente).

Il caso di caralu è emblematico.

BonusVita e BonusVelocità hanno qualcosa in comune? Voilà:

public class Bonus {
public static class BonusVita {}
public static class BonusVelocità {}
}

Dal punto di vista dell'orientamento agli oggetti non solo è ineccepibile (è caralu che conosce il sistema di riferimento) ma è anche significativo: BonusVita e BonusVelocità sono correlati a Bonus. Il problema nasce quando si passa a questo:

public class Bonus {
public static class BonusVita extends Bonus {}

public static class BonusVelocità extends Bonus {}
}

Tutte le relazioni sono almeno nominali (se c'è tra due oggetti allora questi hanno qualcosa in comune). BonusVita ha qualcosa in comune con Bonus per via della clausola extends. Pure BonusVelocità ha qualcosa in comune con Bonus, per via della clausola extends. Poi c'è l'annidamento che dice: BonusVita e BonusVelocità hanno qualcosa in comune con Bonus. E' una ripetizione: "io vado vado al mare"?. No. Non è una questione di design ma di grammatica, grammatica dell'orientamento agli oggetti. Questo è giusto:

public class Bonus {
public static class BonusVita {}

public static class BonusVelocità {}
}

e significa una cosa. Questo è giusto:

public class Bonus {}

public class BonusVita extends Bonus {}

public class BonusVelocità extends Bonus {}

e signifca un'altra cosa. Questo significa qualcosa:

public class Bonus {
public static class BonusVita extends Bonus {}

public static class BonusVelocità extends Bonus {}
}

ma non è giusto così come non lo è "io vado vado al mare" (pur significando qualcosa). Lo stesso si può dire nel caso in cui BonusVita e BonusVelocità siano classi interne a Bonus: anzi, in quel caso abbiamo sul groppone anche la parte relativa alla relazione tra relazione e istanza.

Con l'annidamento si fanno tante cose interessanti. Una sfrutta anche il fatto che l'annidato veda cose che gli altri non vedono. Supponiamo di avere un immutable:

public final class Giovannone {
private final String nome, cognome, soprannome, nomignolo;

come inizializziamo i suoi campi?

public final class Giovannone {
private final String nome, cognome, soprannome, nomignolo;

public Giovannone(String nome, String cognome, String soprannome, String nomignolo) {
this.nome = nome;
this.cognome = cognome;
this.soprannome = soprannome;
this.nomignolo = nomignolo;
}

public String getNome() {
return nome;
}

public String getCognome() {
return cognome;
}

public String getSoprannome() {
return soprannome;
}

public String getNomignolo() {
return nomignolo;
}
}

Non c'è nulla che non vada in questa classe ma dal punto di vista del programmatore è affetta dal morbo "diamine cos'e che devo mettere prima..." perchè tutti gli argomenti hanno lo stesso tipo:

Giovannone g = new Giovannone("Giò", "Gì", "Giù", "Già");

I setter sarebbero molto più significativi (setNome(String), setCognome(String)...eccetera) ma è un immutable e i setter non ci vanno. Morale della favola, noi impacchettiamo il setter ed esprimiamo a chiare lettere che esso offre un servizio a Giovannone con una relazione nominale:

public final class Giovannone {
private final String nome, cognome, soprannome, nomignolo;

public Giovannone(Data data) {
this.nome = data.nome;
this.cognome = data.cognome;
this.soprannome = data.soprannome;
this.nomignolo = data.nomignolo;
}

public String getNome() {
return nome;
}

public String getCognome() {
return cognome;
}

public String getSoprannome() {
return soprannome;
}

public String getNomignolo() {
return nomignolo;
}

public static class Data {
private String nome, cognome, soprannome, nomignolo;

public void setNome(String v) {
nome = v;
}

public void setCognome(String v) {
cognome = v;
}

public void setSoprannome(String v) {
soprannome = v;
}

public void setNomignolo(String v) {
nomignolo = v;
}
}
}

Giovannone.Data data = new Giovannone.Data();
data.setNome("Giò");
data.setCognome("Gì");
data.setSoprannome("Giù");
data.setNomignolo("Già");
Giovannone g = new Giovannone(data);

Non è più breve ma è più semplice: il consenso del programmatore sul significato dell'informazione immessa al fine di creare un Giovannone qualsiasi è informato ciò che teoricamente riduce l'immissione di informazioni errate in un contesto in cui queste non siano altrimenti distinguibili (son tutti String, c'è poco da distinguere). Per la parte che qui interessa, l'annidamento di Data in Giovannone esprime l'esistenza di una relazione tra i Data e i Giovannone, relazione che è stata ovviamente osservata dal programmatore all'atto della traduzione del sistema di riferimento in modello di sistema (usando un linguaggio orientato agli oggetti il secondo salta fuori paro-paro al primo).

E voi, pochi e accaniti sopravvissuti a queste brevi riflessioni :D, cosa ne pensate?

thebol
28-09-2006, 08:22
E voi, pochi e accaniti sopravvissuti a queste brevi riflessioni :D, cosa ne pensate?
che eclipse quando devo inserire dei parametri mi fa vedere sia il tipo che il nome, percui la classe annidata puppa e vince il costruttore :asd:


ps.in caso di costruttori con n mila parametri la cosa potrebbe aver senso, ma la stessa cosa si puo ottenere con una classe esterna, ma in effetti ci vuole piu codice(getter & setter, invece che soli setter) per la classe data.

per quanto riguarda le inner class, si sono potenti per il fatto che riescono ad accedere ai membri private della classe top, ma preferisco girare attorno a questo limite in altra maniera per evitare di avere 2 classi in un unico .java.

Potrebbe venire interessante per una classe astratta, di cui bisogna implementare uno o due metodi che debbano accedere alla parte privata della top-class, ma non so manco se funziona :asd:

PGI-Bis
28-09-2006, 08:55
Avevo il forte sospetto che non sarei riuscito a spiegarmi. Be' com'è il detto: meglio aver tentato e fallito che non aver tentato affatto :D.

thebol
28-09-2006, 10:00
Avevo il forte sospetto che non sarei riuscito a spiegarmi. Be' com'è il detto: meglio aver tentato e fallito che non aver tentato affatto :D.
:|

lovaz
28-09-2006, 10:13
Scusa, ma io ho sempre sentito che inner e nested sono sinonimi,
dove hai letto la distinzione?

PGI-Bis
28-09-2006, 10:26
E' scritto nelle specifiche del linguaggio di programmazione Java (The Java Language Specifications (http://java.sun.com/docs/books/jls/), 3th edition, introduzione al capitolo 8, secondo paragrafo, p. 173 e capitolo 8.1.3, primo paragrafo, p. 181)

caralu
28-09-2006, 10:31
E' scritto nelle specifiche del linguaggio di programmazione Java (The Java Language Specifications (http://java.sun.com/docs/books/jls/), 3th edition, introduzione al capitolo 8, secondo paragrafo, p. 173 e capitolo 8.1.3, primo paragrafo, p. 181)
Ha ragione! cito anche:
JAVA: fondamenti di progettazione software di Lewis & Loftus, cap.5 pag.228

lovaz
28-09-2006, 10:32
Grazie.

Andrea16v
28-09-2006, 10:36
Non sono un fenomeno con Java, ma la spiegazione di PGI-Bis mi pare molto ben fatta, almeno, io ho capito.. :)