PDA

View Full Version : [java] domandina sui Thread e sulle interfacce


aeroxr1
24-05-2011, 18:18
ciao,

i thread mi son da sempre stati abbastanza antipatici e vorrei cercare di farmeli stare più simpatici :D

ho un dubbio che spero possiate togliermi :)

class Pesa {
public static void main(String[] args) {
int[][] m = {
{ 1, 2 },
{},
{ 1 } };
Casella c = new Casella();
int id = 0;
for (int[] s: m)
new MioThread(id++, s, c);
}
}


MioThread è la classe che estende Thread .
Richiamandola all'interno del for i 3 thread vengono avviati uno dopo l'altro o in concomitanza ? cioè mentre è avviato il primo thread parte il secondo o prima di partire il secondo deve finire il primo ?

altra domandina :

((MioThread)Thread.currentThread()).mioId();

MioThread è sempre la classe che estende Thread , ma per usare la funzione currentThread non basta fare Thread.currentThread().mioId() ??? Perchè c'è anche MioThread tra parentesi ?


E ora una domandina sulle interfacce ! :)

mi son sempre chiesto l'utilità delle interfacce , cioè se un'interfaccia contiene solo la dichiarazione dei metodi e nelle classi che la implementano questi metodi vanno definiti che vantaggio c'è ad usarli ? A cosa servono ?


scusate le troppo domande :) ciao !

PGI-Bis
24-05-2011, 22:05
Non farti intimorire, i Thread sono tra gli argomenti più semplici che si possano immaginare. Specialmente da quando ci sono i generici :D.

Nel codice che hai incollato i thread non sembrano essere avviati, mancherebbe infatti l'invocazione del metodo start.

E' tuttavia possibile che nel costruttore di MioThread tu invochi start() che non è una grandissima idea perché alcune regole del linguaggio Java che riguardano l'inizializzazione dei campi si applicano solo dopo che il costruttore ha terminato l'ultima delle sue istruzioni: l'avvio di un Thread all'interno del costruttore può causare l'esecuzione di codice concomitante alla costruzione. Per capire se effettivamente sia un problema bisogna esaminare il codice nel suo complesso.

Supponendo che il metodo start sia invocato, l'avvio dei thread è sequenziale (partono nell'ordine che puoi desumere dal codice del metodo main). La loro esecuzione, cioè quello che capita nei loro metodi run() è concorrente: i thread sono avviati uno alla volta ma ciò che capita nei loro metodi run capita (potenzialmente) nello stesso momento.

Per farti un esempio concreto della differenza, se avessimo:

public class MyThread extends Thread {
private final int id;

public MyThread(int id) {
this.id = id;
System.out.println(id + " E' STATO GENERATO");
}

@Override
public void run() {
System.out.println("IO SONO: " + id);
}
}

Di fronte al main:

public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
MyThread t = new MyThread(i);
t.start();
}
}

Potremmo dire, in forza di una specifica norma del linguaggio di programmazione Java (l'effetto visibile di azioni intra-thread è quello risultante dall'esecuzione del codice nell'ordine in cui appare nel codice sorgente), che vedremo sempre, sempre, sempre la scritta "0 E' STATO GENERATO" apparire prima di quella "1 E' STATO GENERATO" che apparirà prima di quella "2 E' STATO GENERATO". Nulla potremmo invece dire con certezza dell'ordine reciproco delle scritte "IO SONO: 0", "IO SONO: 1", "IO SONO 2", perché i "run" sono eseguiti in concorso.

Il metodo Thread.currentThread() restituisce il Thread che sta eseguendo il metodo o l'inizializzatore in cui si trova l'invocazione.

Chi sia questo Thread dipenda da tutti fuorché dal metodo in cui ne puoi trovare l'invocazione dal che deduciamo che fare un cast sul suo risultato sia un po' rischioso.

Ad esempio:

void metodo() {
System.out.println(Thread.currentThread());
}

Stamperebbe "pippo" se io dicessi:

new Thread("pippo") {
public void run() {
metodo();
}
}.start()

"giovanni" se io dicessi:

new Thread("pippo") {
public void run() {
metodo();
}
}.start()

Pippo e Giovanni se io eseguissi entrambi i thread qui sopra.

Per quanto riguarda le interfacce, in Java esistono per permettere l'ereditarietà multipla - cioè la capacità di un tipo di derivare da più di un tipo dei quali almeno uno non derivato dagli altri.
Per ciò che è ereditabile, contengono solo dichiarazioni di metodi perché il fatto di possedere solo le dichiarazioni ma non i corpi esclude la possibilità che si verifichino dei conflitti tra le definizioni di quei metodi. Questi conflitti infatti non possono che essere risolti con regole arbitrarie.

Esempio in python:

class A:
def stampa(self):
print("A")

class B:
def stampa(self):
print("B")

class C(A, B):
pass

a = C()
a.stampa()

Capita che non ci sia una ragione per cui stampare A sia più logico che stampare B quindi la cosa si risolve imponendo una regola a caso e via.

Se lasci la firma ma togli il corpo non c'è alcuna regola da applicare: chi eredita eredita il metodo e dovrà dire nel suo corpo cosa voglia fare oppure avrà il corpo dell'unica superclasse da cui può derivare.

Dunque interfacce = ereditarietà multipla, solo le firme dei metodi (e quindi non derivazione multipla) perché così non occorre introdurre una regola ad hoc per la risoluzione dell'implementazione del metodo che sarà concretamente invocato.

Questo per quanto riguarda le tecnicalità.

Per l'altro aspetto delle interfacce, cioè quello per cui chi le implementa dichiara di essere compatibile con il tipo dell'interfaccia, valgono le stesse considerazioni che valgono per le classi.

aeroxr1
25-05-2011, 12:02
grazie per la risposta sei stato molto gentile e chiaro :)

il dubbio sui thread è svanito ora :)

sulle interfacce devo rileggere il tutto un pò meglio in quanto ho letto abbastanza di corsa :)

ma la domanda è :

se implemento un interfaccia devo definire e implementare tutte le classi dell'interfaccia che andrò ad usare giusto ? allora non era la stessa cosa se definivo ed usavo quella determinata classe senza implementare l'interfaccia ?

jappilas
25-05-2011, 14:58
<...>
se implemento un interfaccia devo definire e implementare tutte le classi dell'interfaccia che andrò ad usare giusto ? per ogni classe che implementa l' interfaccia dovrai scrivere i metodi che costituiscono quell' interfaccia... ;)
allora non era la stessa cosa se definivo ed usavo quella determinata classe senza implementare l'interfaccia ?non proprio
oltre che nel rendere possibile l' ereditarietà multipla come giustamente diceva PGI-Bis, l' utilità dell' interfaccia sta nei casi in cui si faccia uso del polimorfismo
quando cioè tu abbia degli oggetti che da una parte ridefiniscano il comportamento di metodi presenti nella classe (base) da cui derivano, ma dall' altra siano usati dal restante codice come istanze della stessa classe base - in questo senso puoi pensare un' interfaccia (che in pratica non è altro che una classe avente metodi tutti virtuali puri) come un contratto, tra il codice di un oggetto che svolge determinate funzionalità e il codice che necessita di quelle funzionalità (ma non necessariamente di sapere "chi" li sta implementando in quel momento, a patto che siano definiti correttamente e il contratto sia rispettato)
ti faccio due esempi...
in un gioco come poteva essere Diamonds (rip) puoi avere degli oggetti sul campo di gioco (in quel caso gemme normali, gemme "tiled", bauletti) aventi comportamenti differenziati (come sprite generati in modi diversi o animati a differenti intervalli, ma anche e soprattutto diverse reazioni ai metodi crush() o transform() per realizzare la meccanica di gioco) ma anche richiedenti parecchia funzionalità comune (interazione con la griglia e tra loro stesse, ecc)
in questo caso conviene avere una classe base che implementi quest' ultima E esponga dei metodi che sicuramente verranno ridefiniti per ogni classe derivata - e infatti avevamo Chest, Gem, BigGem ecc che specializzavano AbstractDroppable (che implementava Droppable)
oppure puoi ad esempio realizzare un menu incapsulando il codice corrispondente alle varie opzioni in altrettante implementazioni di uno stesso metodo
void execute(RunLoop loop), in altrettanti oggetti che istanzierai per poi invocare quello appropriato, cosa che migliora sia la leggibilità sia (per esecuzioni ripetute) l' efficienza rispetto a switch e if chain... e infatti avevamo una lista di istanze di NetPlay, Options, Quit, ecc implementanti l' interfaccia MenuAction, e un selectedMenuAction.execute(this); (dove il this era l' oggetto menuLoop corrente, che così l' oggetto action poteva far terminare o meno a sua discrezione)
discorso analogo per la macchina a stati del gioco, dove l' interfaccia GridControllerState era costituita dal metodo
GridControllerState update(Grid grid, ScoreCalculator scoreCalculator)e ad ogni ciclo veniva eseguitocurrentState = currentState.update(...);

PGI-Bis
25-05-2011, 17:19
ma la domanda è :

se implemento un interfaccia devo definire e implementare tutte le classi dell'interfaccia che andrò ad usare giusto ? allora non era la stessa cosa se definivo ed usavo quella determinata classe senza implementare l'interfaccia ?

Sì devi implementare tutti i metodi. La seconda domanda non l'ho capita.

aeroxr1
25-05-2011, 22:29
Sì devi implementare tutti i metodi. La seconda domanda non l'ho capita.

cioè se io nella mia interfaccia x ho :

int pippo (int pesche , String pluto)


e nella classe che implementa questa interfaccia devo implementare il metodo pippo facendo :

class prova implements interfaccia x
{int pippo (int pesche, String pluto)
{implementazione del metodo}
}

non era la stessa cosa se facevo

class prova
{int pippo (int pesche, String pluto)
{implementazione del metodo}
}

senza implementare l'interfaccia x ?

PGI-Bis
25-05-2011, 23:25
Chiarissimo.

Ci sono linguaggi in cui è la stessa cosa - cioè nei quali non useresti l'interfaccia o una superclasse.

Java non è tra questi.

Dal punto di vista del linguaggio, quando una classe Java "extends" un'altra classe o "implements" una o più interfacce, le istanze di quella classe possono essere usate come argomenti delle invocazioni di metodo o costruttore che dichiarano di volere come tipo di argomento uno qualsiasi dei tipi - classi, interfacce - che la classe, direttamente o indirettamente, implementa o estende.

Nell'esempio che fai significa che la "p" di:

prova p = new prova();

può essere, ad esempio, usata come argomento dei metodi che richiedono un tipo "x", un tipo "prova" e un tipo java.lang.Object.

public void unMetodo(prova a)...
public void unMetodo(x a)...
public void unMetodo(java.lang.Object a)...

è un passpartout.

Se la classe prova non implementa l'interfaccia x, pur avendone magari tutti i metodi, non può essere usata come se fosse un tipo di dato x. Ne avrebbe le capacità ma non le manifesta e il compilatore rigetterebbe il tentativo.

demos88
25-05-2011, 23:29
Come ha detto PGI e aggiungo un parere personale:
le interfacce hanno anche un valore "formale" nel definire il comportamento (ovvero i metodi che devono essere forniti) che devono avere determinate classi.
Un esempio banale (non del tutto corretto, ma ha senso)... l'interfaccia ADT (abstract data type) può definire i metodi "inserisci" e "estrai". L'interfaccia ADT sarà poi implementata dalle classi Pila, Catena, Albero... infatti le strutture dati Pila, Catena e Albero prevedono tutte metodi di inserimento ed estrazione, anche se ovviamente i metodi saranno implementati in modo diverso a seconda della struttura. Questo è il significato di implementare una interfaccia: la classe che implementa l'interfaccia "si impegna" a fornire determinate funzionalità, nelle modalità specifiche previste dalla classe.

In realtà ci sono alcuni casi particolari in cui per il compilatore ha senso richiedere che venga implementata una interfaccia, per esempio nel caso dell'interfaccia Serializable. Una classe implementa Serializable (ma di fatto non deve definire nessun metodo particolare) quando si necessita di inserire gli oggetti istanziati da essa in stream. Ma non è il tuo caso...