View Full Version : [Java]: output programma
mercury841
26-06-2007, 18:01
Ragazzi mi spiegate perchè l'output di questo programma è:
B1:3
B1:3
B1:3
true
invece di:
C: 3
B1: 3
C: 3
true
ecco il programma:
class A {
public String f(A other, int n) { return "A:" + n; }
}
class B extends A {
public String f(A other, int n) { return "B1:" + n; }
public String f(A other, long n) { return "B2:" + n; }
}
class C extends B {
public String f(Object other, int n) { return "C:" + n; }
}
public class Test {
public static void main(String[] args) {
C gamma = new C();
B beta = new B();
A alfa = gamma;
System.out.println( alfa.f(beta, 3)) ;
System.out.println(beta.f( alfa , 3)) ;
System.out.println(gamma.f(alfa, 3)) ;
System.out.println( alfa.equals(gamma));
}
}
stai provando a fare la certificazion java? :p
cmq ecco il motivo:
alfa, anche se dichiarato di tipo A, a runtime è un oggetto di tipo C, ed è questo che conta quando si tratta di richiamare i metodi.
Inoltre nella classe C, la funzione f non è un override, bensì un overload. Quindi:
1. quando chiami alfa.f o gamma.f stai chiamando lo stesso metodo.
2. il metodo ereditato da B e quello ridefinito sono due metodi differenti ma con lo stesso nome.
Essendo presenti sia f(Object other, int n) che f(A other, int n), il match migliore per una sottoclasse di A è sicuramente quello ereditato da B, quindi viene chiamato quello sia nel primo che nel terzo caso.
C'è una ragione profonda per cui si è portati a rispondere C:3 alla prima e alla terza, la stessa che ci ha salvato le chiappe dai leoni nella savana poco dopo la nostra discesa dall'albero.
Chi ha scritto quel test o non la conosce, e allora avrebbe dovuto astenersi, o l'ha fatto apposta, e allora quel test non certifica la conoscenza del linguaggio di programmazione Java ma l'attitudine a risolvere rebus e sciarade.
Comunque (e con un sospiro di rassegnazione) la ragione per cui "C:3" è che nella classe C, come peraltro ritengo tu sappia, esistono tre metodi:
public String f(Object other, int n) { return "C:" + n; }
public String f(A other, long n) { return "B2:" + n; }
public String f(A other, int n) { return "B1:" + n; }
Questo per via dell'estensione.
L'enunciato:
alfa.f(beta, 3)
appare in un punto in cui tutti e tre quei metodi sono accessibili (bella forza, sono accessibili ovunque).
Sono anche applicabili? Vediamo.
f(beta, 3), beta è un B, 3 è un int, quindi l'invocazione è per un metodo:
f(B, int)
Per essere applicabile ad un'invocazione un metodo deve avere un numero di parametri pari a quello dell'invocazione (salvo arità) ed ogni parametro deve essere dello stesso tipo o di un supertipo del corrispondente valore usato nell'invocazione. Il metodo deve avere lo stesso nome dell'invocazione, ovviamente. Diamolo per scontato.
Quanti dei metodi "f" contenuti in c hanno due parametri? Tutti e tre.
Quanti dei metodi "f" contenuti in c hanno come primo parametro un tipo che è B o un supertipo di B? Tutti e tre.
Quanti dei metodi "f" contenuti in c hanno come secondo parametro un tipo che è "int" o un supertipo di "int"? Tutti e tre.
Il linguaggio stabilisce che quando ti trovi incartato in questo modo infame tu debba passare alla scelta del "metodo più specifico in assoluto".
Dati due metodi H e K, accessibili ed applicabili ad una stessa invocazione, H è più specifico di K se:
il primo parametro di H è sottotipo del primo parametro di K;
il secondo parametro di H è sottotipo del secondo parametro di K;
il terzo parametro di H ... eccetera eccetera finchè ci sono parametri.
Intendo con sottotipo il più lungo "tipo o sottotipo".
Riprendiamo i tre metodi, accessibili ed applicabili:
public String f(Object other, int n) { return "C:" + n; }
public String f(A other, long n) { return "B2:" + n; }
public String f(A other, int n) { return "B1:" + n; }
f(Object, int) è più specifico di f(A, long)?
Object <: A, no.
f(A, long) è più specifico di f(Object, int)?
A <: Object, vero
long <: int, falso, no.
Ne resta uno per cui incrociamo le dita.
f(A, int) è più specifico di f(Object, int)?
A <: Object, vero
int <: int, vero
f(Object, int) è più specifico di f(A, int)?
Object <: A, falso, no.
f(A, int) è più specifico di f(A, long)?
A <: A, vero
int <: long, vero.
f(A, long) è più specifico di f(A, int)?
A <: A, vero
long <: int falso, no.
Non essendoci metodi più specifici di f(A, int) questo sarà quello invocato.
Dunque se alfa è un riferimento ad un oggetto di tipo C, l'invocazione
alfa.f(beta, 3)
con beta := B e 3 := int, causerà l'invocazione di quello dei tre metodi in C che ha come parametri un A e un int. Il corpo di questo metodo stampa
B1:3
Nel caso in cui ci fossero metodi più specifici di f(A, int) allora o uno è Mandrake o piglia e si legge le specifiche perchè già chiedere 'sta roba è da fuori di testa, figuriamoci andare avanti.
mercury841
27-06-2007, 09:20
grazie a tutti e due, siete stati chiarissimi
ciao:D
mercury841
27-06-2007, 16:18
invece in quest'altro esercizio:
class A {
public int f() { return 0; }
public int f(A x) { return f() + 10; }
}
class B extends A {
public int f() { return 3; }
public int f(B x) { return f() + 20; }
}
class C extends B {
public int f(C x) { return 7; }
}
public class Test {
public static void main(String[] args) {
C gamma = new C();
B beta = gamma;
System.out.println(beta. f (beta));
System.out.println(gamma.f(beta));
System.out.println(211 > 523);
System.out.println(74 & 1);
}
}
perchè l'output è:
23
23
false
0
quando si chiama il metodo beta.f(beta), perchè viene richiamato il metodo public int f(B x) { return f() + 20; } della classe B, nonostante beta sia un oggetto di tipo C?
La riassumo e parecchio.
"beta" è un riferimento di tipo B che punta ad un oggetto di tipo C.
Se non c'è quella che viene orrendamente definita "dereferenziazione", conta il tipo del riferimento e non dell'oggetto riferito.
In java l'unico caso in cui si prende in considerazione il tipo dell'oggetto e non del riferimento è quando si usi l'operatore "." per invocare un metodo.
C'è un punto? No, allora beta è un B. Se ci fosse un punto, seguito dal nome di un metodo, allora beta varrebbe come un C.
Dunque:
beta.f(beta)
vale come:
"il metodo f(B) appartenente al tipo a cui beta fa riferimento (C)"
mercury841
27-06-2007, 19:40
scusa, ma il punto c'è quando invoco il metodo beta.f(beta), forse non ho capito la questione del punto?
beta.f(beta)
prima c'è "beta", poi c'è il punto, quindi sai che devi tener conto non del tipo del riferimento "beta" (che è B) ma del tipo dell'oggetto a cui punta (che è un C). Questo vale per sapere quali metodi possiede "beta".
Posto che qui si invoca uno dei metodi di C, qual'è questo metodo. Tocca fare la manfrina di prima.
E' un metodo che si chiama "f" e accetta un parametro di tipo:
beta, che è un B. Perchè non c'è il punto.
Ai fini della valutazione dei metodi posseduti, beta è un C. Ai fini della valutazione del tipo di parametro, lo stesso beta di prima è un B.
La classe C ha a disposizione tre metodi "f" applicabili, f(A), f(B), f(C). Dei tre è f(B) il più specifico, perchè il tipo di parametro con cui è invocato è B, e quindi è questo ad essere scelto
Naturalmente questo modo di dire "perchè non c'è il punto" non è propriamente tecnico, ma è per capirsi, per avere un appiglio visivo alla faccenda.
Ancora più esplicito. In beta.f(beta) ci sono due pezzi, uno col punto e uno senza:
beta. <- c'è il punto, vale come C
(beta) <- non c'è il punto, vale come B
mercury841
27-06-2007, 21:43
ho capito, grazie, sei stato gentilissimo
ciao
mercury841
28-06-2007, 11:03
scusate ancora ma avrei un altro quesito. Il codice è questo:
abstract class A {
public abstract String f(A other, int n);
public String f(A other, long n) { return "A:" + n; }
}
class B extends A {
public String f(A other, int n) { return "B1:" + n; }
public String f(Object other, int n) { return "B2:" + n; }
public String f(B other, long n) { return "B3:" + n; }
}
class C extends B {
public String f(B other, long n) { return "C1:" + n; }
public String f(C other, int n) { return "C2:" + n; }
}
public class Test2 {
public static void main(String[] args) {
C gamma = new C();
B beta = gamma;
A alfa = gamma;
System.out.println( alfa . f (beta, 4)) ;
System.out.println( alfa . f (beta, 4L));
System.out.println(beta. f ( (Object) alfa , 4)) ;
System.out.println(gamma.f(gamma, 3));
}
}
l'output di questo codice è:
B1:4
A:4
B2:4
C2:3
non riesco a capire perchè "System.out.println(alfa.f (beta, 4L));" stampa A:4 invece di B3:4.
In poche parole perchè invoca il metodo di "public String f(A other, long n) { return "A:" + n; }" nonostante beta sia di tipo B (senza punto)????
Perche' alfa e' un riferimento di tipo A
mercury841
28-06-2007, 12:06
Perche' alfa e' un riferimento di tipo A
scusa ma alfa non è un riferimento di tipo A che punta ad un oggetto di tipo C?
Credo che quella di lovaz sia una svista.
I metodi che devi considerare sono quelli posseduti dall'oggetto a cui punta il riferimento. Per l'invocazione di un metodo, alfa conta come C.
Comunque, per sapere cosa stampa è sufficiente che guardi il capitolo 15.12 delle specifiche del linguaggio di programmazione Java (The Java Language Specifications, 3th ed.)
Lì c'è scritto tutto il procedimento. E hai la certezza che sia descritto com'è veramente.
mercury841
28-06-2007, 12:54
grazie, ciao
FERMI TUTTI.
Ha ragione lovaz. Altro che svista.
Ho confuso la determinazione del metodo da invocare con il metodo invocato e mi chiuderò in una vergine di Norimberga per questo :cry:
E' chiaro. Il compilatore determina quale metodo collegare all'invocazione sulla base del tipo che vede durante la compilazione.
Il punto non c'entra una mazza con la compilazione :fagiano:
Ripeto: HO CONFUSO LA COMPILAZIONE CON L'ESECUZIONE.
Sempre guardare le specifiche prima di parlare, sempre.
The first step in processing a method invocation at compile time is to figure out
the name of the method to be invoked and which class or interface to check for
definitions of methods of that name.
e poi:
If the form is Primary.NonWildTypeArgumentsIdentifier...let T be the type of the Primary expression; then the class or interface to be searched is T
E il tipo di "alfa" è A, non C.
Le specifiche signori, le specifiche. Quelle bisogna guardare. Non fidatevi del primo Pirla-Bis che incontrate su un forum. :muro:
La teoria del punto! Macheccaz... i punti mi devo dare, cinquanta e sulla fronte. :muro:
mad_hhatter
28-06-2007, 17:44
cioè, fatemi capire perchè mi son perso...
nel primo esempio alfa puntava a un oggetto di tipo C e veniva invocato un metodo di C... ora no, perchè??
EDIT:
C eridita il metodo public String f(A other, long n) { return "A:" + n; } di A... quindi suppongo che venga invocato in quanto alfa punta a un oggetto di tipo C, ma non mi pare che tale metodo sia il più specifico in assoluto, data l'invocazione... non capisco
Il primo alfa è un A (in compilazione), ha un solo metodo applicabile, l'unico dichiarato, e il compilatore collega l'invocazione a:
f(A, int)
che è l'unico metodo noto.
Durante l'esecuzione si verifica la dereferenziazione (la teoria del punto :muro:) e il metodo eseguito è la sovrascrittura di f(A, int) dichiarata in B ed ereditata da C.
La faccenda del metodo più specifico è valida ma opera dopo la determinazione di applicabilità dei metodi che è fatta rispetto ai metodi dichiarati nel tipo del riferimento e non nel tipo del valore a cui il riferimento punta. Che è l'esatto contrario della mia teoria del punt e mes :hic:
mercury841
28-06-2007, 21:27
ormai mi sono perso :muro: :muro: :muro: :muro:
E chi ti può biasimare, con la cialtronata che t'ho rifilato si sarebbe perso anche il dottor Livingstone.
Tu segui questo testo:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12
e vedrai che ti si schiarisce tutto. Mi spiace veramente guarda. Non so perchè ma m'è venuto in mente il collegamento dinamico e ho fatto su un pastrocchio tra la risoluzione in compilazione e 'sta roba che avevo in mente solo io. Dimentica tutto quello che t'ho detto. Non lo cancello solo perchè resti a imperitura memoria della coglionaggine del sottoscritto.
mad_hhatter
28-06-2007, 22:12
Il primo alfa è un A (in compilazione), ha un solo metodo applicabile, l'unico dichiarato, e il compilatore collega l'invocazione a:
f(A, int)
che è l'unico metodo noto.
Durante l'esecuzione si verifica la dereferenziazione (la teoria del punto :muro:) e il metodo eseguito è la sovrascrittura di f(A, int) dichiarata in B ed ereditata da C.
La faccenda del metodo più specifico è valida ma opera dopo la determinazione di applicabilità dei metodi che è fatta rispetto ai metodi dichiarati nel tipo del riferimento e non nel tipo del valore a cui il riferimento punta. Che è l'esatto contrario della mia teoria del punt e mes :hic:
scusami, ma continuo a non vedere la differenza tra il primo e l'ultimo esempio... in entrambi i casi alfa e' dichiarato di tipo A, perche' una volta viene dereferenziato (a compile-time o a run-time) e l'altra no? e comunque sia, che senso ha che il compilatore decida una cosa e poi questa venga sovvertita a runtime (posto che a runtime non c'e' alcuna informazione aggiuntiva rispetto al tempo di compilazione - in questo caso specifico)?
Il compilatore trova il metodo in base al tipo in compilazione del riferimento. Una volta trovato il metodo, quello resta. In esecuzione, è invocato il metodo con quel nome e quegli argomenti, per come è definito nel tipo del valore riferito.
Nell'ultimo caso alfa è di tipo A.
System.out.println( alfa . f (beta, 4L));
Tra i metodi dichiarati o ereditati in A, qual'è quello che per nome, numero di argomenti e tipo di argomenti risulta compatibile con l'invocazione:
f(B, long)?
In A c'è solo questo che può gestire quell'invocazione.
public String f(A other, long n) { return "A:" + n; }
Durante l'esecuzione sarà invocato il metodo f(A, long) definito nella classe del valore a cui alfa fa riferimento. Il valore di alfa è di tipo C. La classe C ridefinisce il metodo f(A, long)? No. La classe B, madre di C, ridefinisce il metodo f(A, long)? No. Dunque la definizione che esiste in C è quella data in A e l'esecuzione produrrà la scritta "A:4"
Ora consideriamo lo stesso procedimento applicato al primo esercizio:
System.out.println( alfa.f(beta, 3)) ;
alfa è un riferimento di tipo A.
Tra i metodi dichiarati o ereditati in A, quanti sono compatibili con l'invocazione (beta, 3)? beta è una variabile di tipo B. 3 è un int. Dunque cerchiamo un metodo che possa gestire l'invocazione (B, int).
In A c'è un solo metodo:
public String f(A other, int n) { return "A:" + n; }
A è supertipo di B, int è tipo di int, dunque quest'unico metodo gestirà la nostra invocazione. Quale definizione del metodo f(A, int) sarà invocata?
il riferimento alfa, di tipo A, punta ad un valore di tipo B. La classe B ridefinisce il metodo f(A, int)?
Sì. La definizione di f(A, int) in B è:
public String f(A other, int n) { return "B1:" + n; }
quindi l'esecuzione produrrà la scritta "B1:3".
Lo stesso identico procedimento applicato ad ognuna delle invocazioni di questi esercizi, e di ogni altro esercizio del genere, produce il risultato corretto.
E mi ci gioco barba e baffi! (sento già la schiuma da barba che mi chiama... :D)
mad_hhatter
28-06-2007, 23:10
grazie mille! mentre tu scrivevi (non crocifiggermi...) ho fatto qualche esperimento e sono arrivato alle stesse conclusioni che hai scritto tu.
Visto cosi' e' un po' oscuro (o, per meglio dire, poco intuitivo), ma con un po' di tempo mi leggero' la parte sul "The Java Language Specifications" per capire la ratio di tutto cio'
mad_hhatter
29-06-2007, 08:32
ripensandoci... in effetti inserita nel contesto del polimorfismo la logica secondo cui opera il compilatore e il runtime Java non è così anti-intuitivo...
Suvvia, non crucifiggetevi... io ho risposto di getto, quasi a caso :D
E per fortuna c'ho indovinato :eek:
Togliete pure a me una curiosita': ma tutta questa enigmistica a cosa serve?
Ad insegnare a scrivere codice leggibile solo dalle macchine?
Saluti.
mad_hhatter
29-06-2007, 11:25
Suvvia, non crucifiggetevi... io ho risposto di getto, quasi a caso :D
sborone :D
Togliete pure a me una curiosita': ma tutta questa enigmistica a cosa serve?
a mantenere giovane il cervello :D
scherzi a parte, personalmente mi piace studiare i meccanismi intimi di un linguaggio come Java
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.