Java spiegato ai filosofi

Questo che state per leggere è un introduzione eretica a Java.

Quando parlo di Java non mi riferisco all'isola, chiaramente, ma proprio al linguaggio di programmazione.

Il mio tentativo qui è quello di introdurre un linguaggio di programmazione orientato agli oggetti negli ambienti della filosofia. Questo testo, dunque, è principalmente rivolto a studenti di filosofia o a chi si interessa di questa materia. Il testo verrà per tanto strutturato di modo tale che sia accessibile a queste persone, ma soprattutto che vengano messi in rilievo i punti essenziali di contatto tra Java e la filosofia.

Quello che state per leggere è un testo che vuole iniziare a scrivere una nuova storia, una storia dove la filosofia si incrocia con la tecnologia. Alla fine di questo testo spero di riuscire a dimostrare che dietro Java si annida molta filosofia, logica filosofica e che Java, in realtà, adotta un punto di vista sulla realtà di stampo molto platonico da un lato e aristotelico dall'altro, un punto di vista che potrebbe essere arricchito e migliorato con i più recenti sviluppi della filosofia.

In questo testo intendo seguire tutto il linguaggio dalle basi della programmazione in generale a quella orientata agli oggetti, sino ad arrivare a mostrare quale sia la reale potenza di questo linguaggio, ossia cosa possiamo farci. Infatti programmare significa creare cose con il pensiero e il ragionamento. La programmazione è quel luogo dove la logica diventa produzione di tecnologia.

1) Storia e caratteristiche di Java

Java come linguaggio nasce nei primi anni novanta da un gruppo di ingegneri con a capo James Gosling, il vero fondatore di Java. Java è anche il nome di un isola dell’Indonesia, nota per le sue piantagioni di caffè. Il linguaggio prende il nome proprio da quell'isola, infatti usa la tazza di caffè come logo.

I principi che intende seguire il linguaggio Java sono principalmente cinque:

1) Essere orientato agli oggetti

In questo modo è molto più vicino e simile al mondo in cui viviamo, il quale è fatto di oggetti. Questo aspetto ci interessa parecchio perché è quell’elemento che lega Java all’ontologia filosofica. Ciò è assai più vero oggi con la così detta “ontologia orientata all’oggetto”.

2) Essere robusto e sicuro

La robustezza del linguaggio consiste nella capacità del linguaggio di prevenire gli errori di programmazione.

3) Essere portabile e indipendente da qualsiasi piattaforma

Il linguaggio può essere usato su differenti piattaforme. Si può programmare Java su Linux, Mac e Windows. Mentre esistono, invece, linguaggi di programmazione come .NET che si usano solo nel mondo Windows.

4) Contiene strumenti e librerie

Le librerie vengono di solito importate quando si scrive il codice e ci permettono di avere nuove funzionalità.

5) È progettato per eseguire codici da sorgenti remote

Java, una volta installato sul computer, si compone di tre parti essenziali:

1) Jvm (Java virtual machine)

È una macchina virtuale che svolge la funzione di caricare il codice, verificarlo ed eseguirlo. Grazie a questa macchina virtuale si può usare Java ovunque. In questo senso Java è indipendente dalla piattaforma. La macchina, inoltre, è virtuale, dunque non esiste fisicamente, ma è un tipo di macchina astratta. Spiegherò più avanti la differenza tra il tempo di compilazione e quello di esecuzione del codice. Per il momento ci basta sapere che questo strumento ci permette di caricare il nostro codice Java ed eseguirlo.

2) Jre (java runtime environment)

Questo elemento in Java contiene tutte le librerie necessarie e altri file che la macchina virtuale usa nel tempo di esecuzione.

3) Jdk (Java development kit)

Si tratta di un ambiente di sviluppo usato per creare applicazioni Java.

Java è un linguaggio molto interessante e molto potente, vi permetterà di fare qualsiasi cosa vogliate fare.

Con Java potrete costruire dei siti web, videogiochi, software,

ma, con Android studio, potrete anche creare delle app Android per gli smarthphone.

Veniamo ora alla spiegazione vera di questo linguaggio.

Intendo dividere questa spiegazione in almeno tre parti:

  • nella prima spiego le basi dei linguaggi in generale, utilizzando chiaramente il codice Java;
  • nella seconda spiegherò i fondamenti della programmazione orientata agli oggetti;
  • nella terza spiegherò come si può usare java per creare software, applicazioni web e altro ancora.

La mia spiegazione vorrebbe essere utile per introdurre i filosofi al linguaggio di Java, dunque molto spesso partirò della filosofia per arrivare a spiegare successivamente il linguaggio di Java.

Spero che questo testo mostri come dietro Java si nasconda molto del pensiero filosofico, pensiero che trae origine sin dai tempi dei greci e come Java parti da un punto di vista molto platonico e aristotelico.

2) I primi passi con la programmazione

Filosofo Aden Evens
Aden Evens

Partiamo dal problema a cui avevo accennato già prima: il problema del tempo.

Quanti filosofi hanno speso fiumi di inchiostro sul tema del tempo:

  • se il tempo sia illusione o realtà;
  • come sia fatto il tempo;
  • se esista una differenza tra un tempo soggettivo della coscienza e uno oggettivo della storia;
  • se il tempo sia relativo e in che modo, ecc.

Com'è il tempo in informatica?

La programmazione ha certamente anche la sua visione del tempo. Di questo ne parla, ad esempio, Aden Evens nello scritto Logic of the digital, un libro che consiglio a tutti quelli che dovessero interessarsi un giorno di programmazione.

Esistono due forme di tempo per la programmazione: il compile-time e il run-time. Un conto è quando il codice viene compilato, trasformato in byte-code, in codice macchina, un altro è quando viene eseguito. Prima il codice, al tempo di compilazione, viene verificato, poi viene eseguito e restituisce un output.

Esistono di conseguenza due tipi di errore:

  • un errore nel tempo di compilazione, che consiste normalmente in un errore nel codice che abbiamo scritto, spesso un errore di sintassi;
  • un errore nel tempo di esecuzione.

Prendiamo il caso più semplice e banale. Apriamo il nostro caro Gedit e scriviamo un codice Java di questo tipo:

Codice Java: scrivere Hello World
Esempio codice Java: scrivere Hello World
Filosofo Levi Bryant
Levi Bryant

Questo codice, non essendo scritto in un IDE, ma solamente su un editor di testo simile al classico Notepad++, deve essere necessariamente eseguito da terminale. Notate che il nome della classe (App) deve essere uguale a quello del file (App.java). Andando sul terminale, una volta che vi siete posizionati nella cartella giusta, mettendo cd + directory della cartella, non dovete fare altro che scrivere questo:

1) javac App.java

(javac + nomeFile.java)

Questo per la compilazione del codice, al quale possono seguire delle segnalazioni di errori. Se capitasse, correggete il codice e poi riscrivete sul terminale gli stessi comandi.

2) java App

(java + nomeClasse)

Questo per l’esecuzione del codice nel terminale. Se non ci sono errori a runtime il codice dovrebbe darvi come output “Hello world”, che è la prima classica frase che si scrive nella programmazione quando si comincia ad imparare. Questa frase deve avere affascinato molte persone perché ci regala la magia di vedere il nostro primo risultato nella programmazione. Possiamo stampare ora delle scritte e dei messaggi usando Java.

3) input/output

Un programma è una funzione che prende degli input e restituisce degli output, dunque in Java sarà molto importante comprendere la differenza tra System.in (input) e System.out (output).

Al giorno d’oggi esiste un filosofo, Levi Bryant, che ha pensato una ontologia orientata alle macchine, concependo le macchine come degli assemblaggi che prendono degli input, applicano una serie di trasformazioni e restituiscono degli output.

Secondo Bryant tutto è una macchina.

Ogni oggetto è una macchina, il quale non è altro che un assemblaggio di altre macchine.

Nell'ontologia orientata alle macchine di Bryant il nostro programma scritto con il codice Java sarebbe a tutti gli effetti una macchina, o meglio un assemblaggio di macchine. Chissà cosa direbbe Bryant della Java virtual machine. Il tema del virtuale, del resto, è un tema molto interessante trattato sia dall'informatica che dalla filosofia. Al momento ci interessa sapere che spesso gli informatici e i filosofi intendono cose diverse per virtuale, ma non è detto che non esistano dei punti di incontro.

Incominciamo a vedere bene come funziona questo sistema di output in Java e magari cogliamo l’occasione per stampare con il programma delle pagine note di testi di filosofia. Prendiamo ad esempio questo passaggio famoso dell’Origine della disuguaglianza di Jean Jaques Rousseau:

«Il primo che, avendo cintato un terreno, pensò di dire “Questo è mio” e trovò delle persone abbastanza stupide da credergli, fu il vero fondatore della società civile. Quanti delitti, quante guerre, quanti assassinii, quante miserie ed errori avrebbe risparmiato al genere umano chi, strappando i piuoli o colmando il fossato, avesse gridato ai suoi simili “Guardatevi dal dare ascolto a questo impostore! Se dimenticate che i frutti sono di tutti e la terra non è di nessuno, siete perduti!”»

(Rousseau, Jean-Jaques, L’origine della disuguaglianza, Feltrinelli, Milano, 2009, p.72)

Per scrivere in Java questo famoso passaggio intendo usare un IDE.

È molto scomodo dover far eseguire sempre il codice dal terminale inserendo i comandi che ho citato sopra. È molto meglio affidarsi ad un IDE, tanto più che questo ci aiuta a capire quali sono gli errori nel nostro codice, ci aiuta a correggerlo, prevenendo l’errore e spiegandoci in cosa consiste l’errore che facciamo. Gli errori sono di diverso tipo: parentesi dimenticate, variabili di un tipo a cui si assegnano valori di un altro tipo, ecc.

Esistono diversi IDE per scrivere codice Java, ma i principali sono tre:

Netbean, Eclipse, Intellij. Intellij è forse il migliore, ma se non siete disposti a spendere vi ritroverete una versione community molto limitata.

La soluzione gratuita più razionale da scegliere credo sia Eclipse.

Bisogna precisare che esistono almeno due versioni di Java:

  • JSE (java standard edition) e JEE (java enterprise edition). Eclipse ci permette di usare gratuitamente la versione JEE o j2ee.
  • Con la versione standard (JSE) possiamo scrivere codice java e costruire, ad esempio dei software usando le librerie swing e awt.
  • Con la versione enterprise (JEE), invece, possiamo anche avventurarci nel mondo web grazie a server come Tomcat.

Torniamo ora al nostro problema originario: dare un print del passaggio dell’opera di Rousseau, usando un sistema di output di Java in un IDE come Eclipse.

Entrando in Eclipse si crea un nuovo progetto, il quale verrà salvato nella eclipse-workspace. Il progetto si compone di varie parti: troviamo principalmente una cartella sorgente (src), dove verranno salvati tutti i file e l’insieme delle librerie di Java che costituiscono il java runtime environment (jre).

Nella cartella di sorgente si crea uno o più package, i quali consistono nelle cartelle dove vorranno salvate le classi. In seguito si crea una classe, che è definita come classe eseguibile, la quale ci permetterà di scrivere il nostro codice. Dunque il nostro codice avrà questo aspetto:

Linguaggio Java, codice di esempio.
Linguaggio Java, codice di esempio.

System.out è un sistema con il quale possiamo restituire degli output nel codice.

  • “+” serve per concatenare le nostre righe di testo, meglio note in programmazione come stringhe.
  • “\n” è una funzione che serve sostanzialmente per andare a capo.

Oltre a poter restituire degli output, nel codice possiamo fare in modo che l’utente dia degli input. Per fare questo dobbiamo in primo luogo importare uno Scanner con la funzione “import”. Scriviamo nel codice “import java.util.Scanner”. Successivamente inseriremo lo Scanner come un nuovo oggetto, il quale ci servirà per leggere nella riga il dato che viene inserito dall'utente. Lo Scanner usa il sistema System.in per gli input. L’input dell’utente potrà infine essere restituito sotto forma di output con il sistema System.out.

Il codice che ho descritto apparirà grosso modo in questa maniera:

Linguaggio Java: System.out
Linguaggio Java: System.out

In alto abbiamo importato la libreria dello Scanner. Nella classe input abbiamo il nostro metodo principale (main), dentro il quale troviamo tutto il codice necessario per gli input e gli output.

4) le variabili

Partiamo dal concetto generale della variabile che potete trovare anche in matematica o in logica. Se avete realmente studiato filosofia, dovete conoscere almeno la logica matematica, visto che i padri della logica matematica sono tutti filosofi.

Nella logica predicativa una variabile si usa come simbolo che può assumere differenti valori. La variabile avrà come valore una costante individuale. Se prendo la formula Px, P è una lettera predicativa e x è una variabile. Se P = essere dei pescatori, possiamo sostituire alla x differenti valori, ma solo se la costante individuale sarà elemento dell’insieme P, ossia se è un pescatore, allora la formula sarà vera. Per cui scriviamo:

Linguaggio Java: variabili
Linguaggio Java: variabili

Nella programmazione informatica la variabile è un contenitore di dati.

Si chiama variabile perché può cambiare il suo valore in punti differenti del codice. In realtà la programmazione è piena di variabili: oggetti, array, costanti, in un certo senso, sono tutte delle variabili. La cosa più strana da capire, forse, è come possa una costante essere una variabile. In verità il concetto di variabile della programmazione è simile a quello matematico, ma le due cose non coincidono affatto. Nella logica, ad esempio, sarebbe un errore confondere una variabile con una costante individuale.

In programmazione nelle variabili troviamo principalmente due parti: una destra e una sinistra.

  • A destra troviamo il tipo e il nome della variabile.
  • A sinistra troviamo il valore della variabile.

Nella scrittura di una variabile possiamo dunque distinguere almeno due processi:

  • la tipizzazione della variabile, ossia l’assegnazione di un tipo alla variabile;
  • l’inizializzazione della variabile, ossia l’assegnazione di un valore alla variabile.

Vediamo un esempio concreto di variabili:

Linguaggio Java: esempio di variabile
Linguaggio Java: esempio di variabile

Nel method “main” trovate una serie di variabili, per la precisione tre:

number1, number5 e somma.

La terza variabile somma le prime due variabili a cui sono stati assegnati come valori dei numeri interi. La prima variabile, ad esempio, si compone di un tipo (int), un nome di variabile (number1) e un valore (34).

Il nome della variabile non penso dia problemi di comprensione. In generale sappiate che in programmazione si cerca di assegnare sempre dei nomi intuitivi, dunque non si chiama la variabile di un numero “x”, ma la si chiama “numero” o “numeroUno”.

Non si scrivono in programmazione parole staccate le une dalle altre, dunque per distinguere due parole differenti si usano due metodi: il camelCase e il PascalCase (da Pascal, il filosofo, al quale hanno dedicato anche un linguaggio di programmazione che porta il suo nome, appunto Pascal).

Le parti più importanti della variabile sono in realtà il tipo e il suo valore.

Partiamo pure dal tipo della variabile. Vi dirò qualcosa di sconvolgente: i tipi che si usano in programmazione li hanno inventati i filosofi. Con questo intendo dire che la nozione di tipo viene dalla filosofia. Se avete studiato la filosofia analitica vi verranno certamente in mente termini come “type” e “token”.

Sappiate che lo stesso linguaggio si usa nella programmazione e il significato di questi termini non muta più di tanto. In filosofia, in generale, si definisce il “type” l’universale e il “token” il particolare. In questo senso, per ogni cosa gialla, ci sarà il giallo particolare delle cose, esemplificato nella cosa, ossia il particolare, mentre ci sarà anche l’universale giallo, ossia il giallo come tale o in sé. Siamo nel pieno dibattito sugli universali, sulla possibile esistenza degli universali.

Notiamo che gli universali sono i tipi e certamente Java, adottando dei tipi, assume gli universali come modello.

Uno dei filosofi contemporanei che difendono gli universali è David Armstrong.

In opposizione al binarismo type/tocken i filosofi hanno concepito il concetto di tropo (trops), che rappresenta un individuale irripetibile. Questo concetto è stato criticato da diversi filosofi di essere solo una mescolanza di type e tocken. Notiamo, dunque, che Java assume questa prospettiva sugli universali, che tra poco approfondiremo, ma che è filosoficamente solo una prospettiva.

Non dico che i tipi vengono dalla filosofia semplicemente per una coincidenza di termini. La verità è che i tipi sono stati introdotti nella programmazione a partire dalla teoria dei tipi di Bertrand Russell. Non che Russell avesse concepito la sua teoria per questo fine, ma di fatto i tipi di Russell sono diventati un sistema nella programmazione che ci permette di distinguere le variabili in base alla tipologia del dato. Non dimentichiamo che la variabile nella programmazione è il tipo di dato che occupa un certo spazio nella memoria.

Vediamo ora questa famosa teoria dei tipi di Russell.

La teoria dei tipi ha uno scopo ben preciso: rispondere al paradosso russelliano sull'insieme di tutti gli insiemi. Se esistesse un insieme di tutti gli insiemi, non sarebbe esso stesso un insieme? Se la risposta è affermativa, allora non dovrebbe contenere oltre a tutti gli altri insiemi, anche se stesso, in qualità di insieme, come suo elemento?

A questo punto si danno solo due casi:

  • o davvero questo insieme contiene se stesso come suo elemento, dunque fa parte di quegli insiemi detti ad “autoingerimento”;
  • o questo insieme non contiene se stesso come suo elemento, dunque fa parte di quegli insiemi detti non ad “autoingerimento”.

In entrambi i casi avremo il risultato paradossale che l’insieme di tutti gli insiemi sarebbe contenuto in un altro insieme. Come se ne esce da questo problema?

Bertrand Russell pensa la teoria dei tipi a questo proposito.

Diamo la parola direttamente a Russell, citando un passaggio nell’appendice della famosa opera: I principi della matematica.

«Ogni funzione proposizionale Ф (x), così si afferma, possiede, oltre al suo sistema di valori (range) di verità, un sistema di valori di significatività, ossia un sistema entro cui deve cadere x se vogliamo che Ф (x) risulti comunque una proposizione, vera o falsa che sia.

Questo costituisce il primo punto nella teoria dei tipi.

Il secondo è, che i sistemi di valori di significatività formano dei tipi, ossia, se x appartiene al sistema di valori di significatività di Ф (x), allora esiste tutta una classe di oggetti (il tipo di x), i quali devono appartenere essi pure al sistema di valori di significatività di Ф (x), comunque possa variare Ф ; tale sistema di valori è sempre o un tipo singolo o una somma di diversi tipi completi.

Il secondo punto è meno preciso del primo, e il caso dei numeri introduce delle difficoltà; ma la sua importanza e il suo significato diverranno, spero, più evidenti nel seguito della presente ricerca.»

(Russell, Bertrand, I principi della matematica, Bollati Boringhieri, Torino, 2011, p.713)

Bertrand Russell ci spiega che i tipi hanno due ruoli, ma di questi due a noi interessa solamente il secondo.

Tutto ciò che non è un sistema di valori, spiega Russell, è un individuo. Normalmente diciamo individuo la singola persona. Qui, invece, si parla anche di classi. Qui Russell parla di “classi come uno” riferendosi alle classi che contengono individui come oggetti di vita quotidiani, quali le sedie, i tavoli, ecc.

Il primo tipo, dunque , è il tipo individuo. Il secondo tipo sarà costituito, invece, dalle classi di individui. Quello successivo, dalle classi delle classi di individui.

Il procedimento è molto semplice:

lo scoiattolo volante è un roditore, il roditore è un animale. Il singolo scoiattolo appartiene a una classe di scoiattoli, ma questa classe appartiene alla classe dei roditori, la quale appartiene alla classe degli animali.

Ma i tipi per Russell non si riferiscono solo alle classi. Russell pensa anche i numeri e le proposizioni come dei tipi e si chiede se non si debbano distinguere persino dei tipi differenti tra le proposizioni. In tal senso i numeri sono del tipo “numero” e le proposizioni sono del tipo “proposizione”.

Java ragiona allo stesso modo.

Esistono differenti tipi di tipi: esistono tipi di classe (es. Persona, String, Integer, Cavallo, App, ecc.), ma esistono anche dei tipi primitivi (boolean, int, char, double, ecc.).

Questi sono tutti i tipi che si usano in Java per le variabili:

1) I tipi non primitivi e tipi classe: sono tipi che si scrivono con la maiuscola. Delle classi parlerò meglio più avanti, per ora sappiate che se il tipo ha la lettera maiuscola è una classe. Noi possiamo inventare delle classi, ma esistono già delle classi predefinite come la classe Object, la classe String, la classe Integer, la classe Array, ecc…

Tra questi tipi il più importante è sicuramente il tipo String, il quale ci permette di usare delle stringhe, ossia insiemi di caratteri e numeri. Per esempio possiamo scrivere:

String frase = “Questa è una stringa”;

2) I tipi primitivi:

a) Il tipo booleano (boolean). Il booleano viene chiaramente dalla logica e può avere come valori 1 o 0. Boole aveva preso la vecchia logica filosofica degli antichi, come Aristotele e Crisippo, per dargli una struttura matematica. George Boole assegna al vero il valore 1 e al falso il valore 0. Java, tuttavia, rimane fedele all'origine filosofica, prima della svolta matematica. Java, dunque, non usa 1 o 0, ma direttamente true e false. Ogni variabile è un dato che occupa uno spazio nella memoria: il boolean corrisponde ad 1 bit e ha come valore di default false, ossia 0.

Esempio in Java:

boolean luceAccesa = true;

b) I tipi numerici:

1) Il tipo carattere. Il tipo carattere è scritto char e corrisponde al singolo carattere. Il tipo carattere occupa 16 bit o 2 byte.

Esempio in java:

char lettera = ‘C’;

2) I tipi integrali: Un tipo dei tipi numero.

a) Il tipo intero: Che contiene una serie di tipi di numeri interi ed è nel tipo integrale.

1) Il tipo byte. È un tipo di una variabile che può assumere valori da -128 a 127 e occupa esattamente un 1 byte di memoria.

Esempio in java:

byte b = -10;

2) Il tipo short. Il suo valore minimo è -32,768, mentre il massimo è 32,767. Short occupa 2 byte di memoria.

Esempio in java:

short s = 2000;

3) Il tipo int. Il range di valori del tipo int va da - 2,147,483,648 (-2^31) a 2,147,483,647 (2^31 -1). Int occupa 4 byte.

Esempio in java:

Int numeroIntero = 3;

4) Il tipo long. Il range di valori di long va da -9,223,372,036,854,775,808(-2^63) a 9,223,372,036,854,775,807(2^63 -1). Long occupa 8 byte.

Esempio in java:

long l = 10000L;

b) I tipi non interi, con la virgola.

1) Il tipo float. I valori del range float non hanno limiti. Float occupa 4 byte di memoria.

Esempio in Java:

float f = 224.6f;

2) il tipo double. I valori del range di double non ha limite. Double occupa 8 byte.

Esempio in Java:

double d1 = 23.4;

Come si può ben vedere i tipi qui funzionano nel modo in cui li aveva pensati Bertrand Russell. Ogni variabile deve avere un tipo, altrimenti Java non sa nemmeno a cosa ci riferiamo. Se proviamo a passare ad un tipo all'altro dobbiamo usare delle tecniche specifiche, altrimenti Java ci dà degli errori, essendo le variabili di tipi diversi. Ogni tipo, come diceva Russell, ha un range di valori. Questo è vero anche in Java, così come è vero che i tipi hanno una gerarchia. Troviamo, come ho spiegato, in cima alla gerarchia una distinzione tra tipi primitivi e tipi classe.

5) Gli operatori

Esistono nei linguaggi di programmazione degli operatori matematici e degli operatori logici. Dal punto di vista filosofico sono molto più rilevanti gli operatori logici di quelli matematici, perciò su quelli matematici non dirò molto.

Gli operatori matematici più usanti nella programmazione sono gli operatori di somma, sottrazione, moltiplicazione e divisione.

Potete ammirarli uno ad uno in questo codice:

package primoProgetto;

public class OperatoriMatematici {

	public static void main(String[] args) {
		
		int numeroUno = 1;
		
		int numeroDue = 2;
		
		int somma = numeroUno + numeroDue;
		
		System.out.println(somma);
		
		System.out.println("===============================");
		
		double numeroTre = 3.0;
	
		double numeroQuattro = 4.0;
		
		double sottrazione = numeroQuattro - numeroTre;
		
		System.out.println(sottrazione);
		
		System.out.println("===================================");
		
		short numeroCinque = 5;
		
		short numeroSei = 6;
		
		int moltiplicazione = numeroCinque * numeroSei;
		
		System.out.println(moltiplicazione);
		
		System.out.println("=====================================");
		
		byte numeroSette = 7;
		
		byte numeroOtto = 8;
		
		double divisione = numeroOtto / numeroSette;
		
		System.out.println(divisione);
		

	}

}

Vediamo ora, invece, gli operatori della logica.

Troviamo come operatori della logica l’identità (“=”) e gli operatori booleani and, or, not. Non credo ci sia molto da dire sull'uguaglianza, tranne due cose:

a) Quando scrivete “=” state assegnando un valore ad una variabile, non state comparando dei valori. Per dire che un certo valore è uguale ad un altro dovete scrivere un doppio uguale “==”.

b) Quando volete comparare per uguaglianza due numeri potete scrivere “==”, ma se volete fare la stessa cosa con le stringhe dovete scrivere in maniera differente usando la funzione: “.equals()”. Esempio: a.equals(“Stringa”).

Molto più interessanti dal punto di vista filosofico sono gli operatori and, or, not. Chi ha studiato filosofia, e perciò ha studio logica matematica, necessariamente conosce i connettivi logici della congiunzione, della disgiunzione e della negazione. Questi operatori logici risalgono al logico stoico Crisippo e sono stati matematizzati successivamente dai filosofi e matematici come Boole, Peano, Russell e Frege.

In filosofia troviamo questi operatori scritti in questo modo:

Filosofia: operatori della logica
Filosofia: operatori della logica

In Java gli operatori sono gli stessi, ma chiaramente cambiano di simboli:

IN Java gli operatori and, or e not
Java gli operatori and, or e not

Nella logica atomica di Russell ogni singola formula consiste in una formula atomica (es. A), mentre ogni combinazione di formule tramite operatori logici costituisce una formula molecolare (es. A ᐱ B). I valori di verità delle formule molecolari dipendono dai valori di verità delle formule atomiche e sono definiti, a seconda del tipo di operatore, dalle tavole di verità. Le tavole della verità della logica sono state ben definite, in modo rigoroso, con il Tractatus di Wittgenstein.

Per capire le tavole della verità usiamo il metodo dell’algebra booleana, dove gli operatori logici sono definiti nei termini degli operatori matematici. L’and corrisponde alla moltiplicazione, l’or all’addizione e il not alla sottrazione.

Sapendo che vero = 1 e falso = 0, si possono definire le seguenti tavole:

1) a b a * b

a = 1 b = 1 a * b = 1 * 1 = 1

a = 1 b = 0 a * b = 1 * 0 = 0

a = 0 b = 1 a * b = 0 * 1 = 0

a = 0 b = 0 a * b = 0 * 0 = 0

2) a b a + b

a = 1 b= 1 a + b = 1 + 1 = 1

a = 1 b = 0 a + b = 1 + 0 = 1

a = 0 b = 1 a + b = 0 + 1 = 1

a = 0 b = 0 a + b = 0 + 0 = 0

3) 1 – a

a = 0 1- a = 1 – 0 = 1

a = 1 1 – a = 1 – 1 = 0


Tutte queste tavole della verità servono molto in Java, anche se in questo momento potrebbe non sembrarlo, perché devono essere tenute presenti quando si usano le variabili nei parametri di determinate istruzioni.

6) I principi della programmazione

La maggior parte dei linguaggi di programmazione, che si tratti di Java, Python, C, javascript, Php o Perl, si basa su dei principi che sono fondamentalmente tre:

1) La sequenza: il codice viene letto dalla macchina dall’alto vero il basso e da sinistra verso destra.

2) La selezione: certe porzioni di codice vengono eseguite solo se certe condizioni sono verificate.

3) L’iterazione: certe porzioni di codice o istruzioni vengono eseguite un certo numero di volte fin tanto che una data condizione è vera.

Sulla sequenza non avrei altro da aggiungere se non il fatto che ogni istruzione deve chiudere con un punto e virgola. Questo permette di dire al programma dove finisce la singola istruzione. Per il resto gli elementi più importanti della programmazione in generale solo i selettori e gli iteratori o anche detti “loop”.

a) I selettori

I selettori sono determinate istruzioni del codice che vengono eseguite solo se certe condizioni sono verificate. Per capire meglio i selettori partiamo pure dalla filosofia. Nella logica matematica che si usa in filosofia troviamo i condizionali, che sono molto simili ai selettori della programmazione.

Un esempio di condizionale:

A → B

A = oggi piove, B = oggi porterò l’ombrello, A → B = Se oggi piove, allora porterò l’ombrello.

Esistono nella programmazione in generale almeno tre selettori: gli if/else, gli operatori ternari (qualche volta li trovate chiamati proprio “condizionali”, come in logica), gli switch.

Partiamo pure dal caso più semplice, il caso degli if/else.

Se prendiamo l’espressione logica che ho scritto prima, possiamo scriverla in Java in questo modo:

Condizionale if di Java
Condizionale if di Java

Gli if, dunque, funzionano come tutti quegli enunciati della forma “se, allora”. Quello che è scritto tra le parentesi tonde è la condizione, mentre quello che vedete scritto tra le parentesi graffe è il codice che viene eseguito se la condizione è verificata.

La condizione, dunque, funziona come un booleano. La prima parte costituisce la testa, mentre quello che si trova tra le parentesi graffe è il corpo. Gli else, invece, rappresentano l’opzione “altrimenti”.

Ritornando al nostro esempio di logica prima possiamo ora scrivere:

Condizione in Logica
Condizione in Logica

In Java possiamo scrivere questa formula con l’if/else in questo modo:

Esempio di Condizione in Java
Esempio di Condizione in Java

Notate che non devo negare la variabile “b”, potrei farlo se intendo dire che “Oggi porterò l’ombrello” è un enunciato falso, ma in questo caso non serve. Gli operatori ternari funzionano grosso modo allo stesso modo, ma, cosa curiosa, riescono ad esprimere la stessa cosa usando la domanda. Quello che ho scritto prima in ternario diventa:

a.equals(“Oggi piove”)? “Oggi porterò l’ombrello” : “Oggi non porterò l’ombrello”;

Le cose si complicano quando abbiamo molte cose da verificare. In questo caso possiamo o costruire una serie di else if, oppure utilizzare lo switch. Vediamo tutte e due i metodi.

Con il primo scriveremo una struttura di questo tipo:

if( x > 4) {

risposta = “x è maggiore di 4”;

}

else if(x != 3){

risposta = “x non è uguale a tre”;

}

else if(x == 3 || x == 4){

risposta = “x è o 4 oppure 3”;

}

else{

risposta = “x è minore di 2”;

}

System.out.println(risposta);

Usare questo metodo può portare via molto tempo e ci obbliga a scrivere molto codice. Un metodo più veloce consiste nell'usare uno switch, ma si può usare solo se vogliamo fare comparazioni di uguaglianza. Il caso che preferisco dello switch è quello dei dadi.

Dunque creiamo un dado in Java assegniamo dei risultati a seconda del lancio.

package primoProgetto;

import java.util.Random;

public class Dado {

	public static void main(String[] args) {
		
      Random dice = new Random();
       
      int numRandom = dice.nextInt(6) + 1; 
      
      
      
      switch(numRandom) {
      
      case 1:
    	  
    	  System.out.println("Avaria");
    	  
    	  break;
    	  
      case 2: 
    	  
    	  System.out.println("due gittata");
    	  
    	  break;
    	  
      case 3:
    	  
    	  System.out.println("tre gittata");
    	  
    	  break;
    	  
      case 4:
    	  
    	  System.out.println("4 gittata");
    	  
    	  break;
    	  
      case 5: 
    	  
    	  System.out.println("Aggiungi una palla di cannone");
    	  
    	  break;
    	  
      case 6:
    	  
    	  System.out.println("Colpito");
      
          break;
      
      }
		
		

	}

}

In questo esempio ho costruito una dado creando un oggetto Random.

Poi ho definito una variabile di tipo intero per avere il mio numero casuale che viene definito come un numero da 0 a 5, al quale aggiungo 1 per avere il classico dado a sei facce. Random prende sempre un numero da 0 tendente all'1, ma che non è mai uno. Per questo prima deve diventare un intero e poi bisogna aggiungere una unità a quel numero. Lo switch, invece, funziona in questo modo: tra le parentesi tonde si inserisce una condizione; dopo di che sono definiti una serie di casi (case), all'interno del corpo, che imposta una serie di opzioni; ogni volta il programma vede a quale caso corrisponde la condizione e successivamente esegue un codice corrispondente. Quello che ho creato io è un classico dado da cannone che potete trovare i qualsiasi gioco come Warhammer o giochi coi pirati come Jamaica. A seconda del risultato del dado il cannone potrà aver colpito il bersaglio, essere andato in avaria, avere una palla in più o aumentare la gittata di un tot.

I selettori sono una delle parti più interessanti dell’intera programmazione, se vediamo la cosa dal punto di vista filosofico e logico.

Io penso che, la maggior parte degli argomenti che troviamo nella filosofia possono essere scritti nella programmazione usando i selettori. In questo modo lasceremo che sia il computer a fare il suo calcolo logico, ma ovviamente il computer non ha i valori di verità dei singoli enunciati logici.

Cominciamo da due casi semplici della logica come il modus ponens o il sillogismo ipotetico.

Il modus ponens in logica si scrive in questo modo:

Esempio di modus ponens
Esempio di modus ponens
package primiEsempi;

public class ModusPonens {

	public static void main(String[] args) {
		
        String a = "metterò molto impegno";
        
        if(a.equals("metterò molto impegno")) {
        	
        	String b = "vincerò la gara";

           System.out.println(b);
        }
		
      

	}

}

Non è possibile seguire proprio alla lettera la struttura del ragionamento, ma chiaramente il risultato è lo stesso. Qui definiamo una variabile, assegnandogli un valore, poi mettiamo il nostro if con quella variabile come condizione, ottenendo così il nostro risultato desiderato. Vediamo ora il caso del sillogismo ipotetico.

Il sillogismo ipotetico si scrive in logica in questo modo:

Il sillogismo ipotetico
Il sillogismo ipotetico

In Java questo sillogismo possiamo scriverlo in questo modo:

Esempio di sillogismo scritto in Java
Esempio di sillogismo scritto in Java

Prendiamo questa volta un argomento di un vero filosofo. Mi capita sotto mano un famoso argomento discusso negli ultimi anni in filosofia e che ha una pessima fama, perché l’aria tira in filosofia in maniera diversa oggi. Una volta il vento soffiava per gli idealisti, oggi i filosofi sono realisti, per la maggior parte. Parlo del “Bad argument” descritto nelle pagine del recente libro di John Searle: Seeing things as they are. Scriviamo l’argomento in forma canonica in questo modo:

1) Sia nel caso di esperienza veridica, sia in quello dell’allucinazione esiste un elemento qualitativamente soggettivo nell'esperienza visiva.

2) In tutti e due i casi l’elemento è lo stesso.

3) In entrambi i casi vediamo sempre qualcosa.

4) Dal momento che nell'allucinazione non esiste alcun oggetto nel modo esterno, perché altrimenti non sarebbe allucinazione, dobbiamo concludere che abbiamo a che fare con un mero dato sensoriale e non un oggetto esterno reale.

5) A causa di quanto asserito nella seconda premessa dovremmo dire quel che abbiamo detto per l’allucinazione anche per il caso dell’esperienza veridica. Ossia che in entrambi i casi ci relazioniamo sono con dati sensoriali e non oggetti esterni reali.

6) Ne consegue che nella percezione non entriamo mai in contatto con entità esterne reali.

Questo è il modo in cui penso si possa tradurre in Java questo argomento:

package primoProgetto;

public class BadArgument {

	public static void main(String[] args) {
		
		String o = "oggetto come è soggettivamente percepito dal soggetto";
		
		String oEsperienzaVeridica = o;
		
		String oEsperienzaAllucinatoria = o;
		
		boolean oEsperienzaAllucinatoriaEst = false;
		
		boolean oEsperienzaVeridicaEst = true;
		
		if(oEsperienzaVeridica.equals(oEsperienzaAllucinatoria)) {
			
			if(oEsperienzaVeridica != null && oEsperienzaAllucinatoria != null) {
				
				if(oEsperienzaAllucinatoriaEst == false) {
					
					o = "dato sensoriale";
				}
			}
		}
		
		if(o.equals("dato sensoriale")) {
			
		    oEsperienzaVeridicaEst = false;
		}

	   System.out.println("L'oggetto della percezione è un " + o + " e " + ( oEsperienzaVeridicaEst? "Non tutto ciò che percepiamo sono dati sensoriali" : "Tutto ciò che percepiamo sono dati sensoriali"));
	
	}

}

Prima ho definito come variabile stringa l’oggetto della percezione, definendolo come elemento soggettivo. Poi ho definito altre due variabili, una per il caso dell’esperienza veridica e l’altra per quello dell’allucinazione, ponendole entrambe uguali all'oggetto. In questo modo dico che il loro oggetto è identico. Poi ho definito due variabili booleane, una per l’esistenza esterna dell’oggetto in caso di esperienza veridica e l’altra per l’esistenza esterna dell’oggetto in caso di allucinazione.

Dichiarate le variabili parto con i controlli dei selettori. Prima chiedo se l’oggetto della percezione veridica e di quella allucinatoria sia lo stesso. Poi chiedo se vi sia un oggetto dell’esperienza, dunque se questo oggetto non sia nullo. Dopo chiedo se sia falso che esista un oggetto esterno nel caso dell’allucinazione e deduco che, se è vero questo, ne consegue che quell'oggetto è solo un dato sensoriale. Controllo anche se l’oggetto è un dato sensoriale, essendo lo stesso sia per l’esperienza veridica che per quella allucinatoria, cambio il valore booleano sulla natura esterna dell’oggetto nell’esperienza veridica e lo metto come falso. Per finire stampo la conclusione del mio argomento.

b) Gli iteratori

Gli iteratori costituiscono l’ultima parte fondamentale della programmazione in generale. L'iteratore è meglio conosciuto in inglese con il nome di “loop”. Un loop esegue una certa porzione di codice un certo numero di volte, fintanto che una certa condizione è verificata. In un certo senso potrebbe essere visto come un if, con la differenza che si ripete. Esistono almeno quattro tipi di loop: while, do-while, for, for-each.

Il while si compone di: una variabile che è sempre dichiarata prima del while stesso; di condizioni scritte nelle parentesi del while, le quali pongono dei limiti al loop, facendo sì che questo non si ripeta letteralmente all'infinito; un incremento o un decremento della variabile, che viene dichiarato all'interno del corpo del while; una porzione di codice da eseguire.

Questo è un while in Java:

while in Java
while in Java

Prima è dichiarato il numero come intero e inizializzato come zero. Fintanto che il numero è minore di 10, il numero verrà stampato. Visto che la variabile viene incrementata (numero++) ad ogni ciclo, ne consegue che dopo il nono ciclo si ferma e non esegue più il codice, perché il numero sarebbe uguale a 10, a qual punto.

Il do-while differisce dal while semplicemente perché prima esegue almeno una volta il codice e poi verifica la condizione del while, facendo partire in caso il ciclo successivo. Dunque, a differenza del while, il codice viene almeno eseguito una volta.

Un esempio do while in Java è il seguente:

do while in Java
do while in Java

Il loop for, rispetto al while, è molto differente. Nel loop for la variabile viene dichiarata direttamente nelle parentesi assieme alle condizioni e assieme all'incremento o al decremento della variabile. Programmiamo una bomba con for in questo modo:

loop for
loop for

Il loop for conta alla rovescia da 10 a 0, ma quando arriva a 0 ci fa comparire il messaggio dell’esplosione.

Il for-each in Java si può usare per creare oggetti a cui assegnare dei valori. Per esempio possiamo scrivere:

for-each
for-each

Nei loop troviamo altre due funzioni interessi che sono “break” e “continue”.

  • “Break” serve per interrompere un loop ad un certo punto dei suoi cicli.
  • “Continue” serve per dire al codice di saltare una parte e continuare.

I loop forse appariranno la parte di Java meno collegabile con quello che si studia in filosofia. Tuttavia, se si tiene presente quello che ho detto all'inizio, in realtà il loop è semplicemente il vecchio condizionale che viene ripetuto più di una volta.

Il problema del loop è il problema fondamentale. Se pensate al teorema dell’indecidibilità della logica predicativa di Alonzo Church e Alan Turing, vi ricorderete che Turing sosteneva che la logica predicativa è indecidibile perché se pensassimo una macchina a cui chiediamo per ogni formula della logica, se questa è valida oppure no, questa macchina continuerebbe a girare senza fornire alcuna risposta. Questo continuare a girare, a tornare su se stesso, è l’essenza del loop.

7) Le classi e gli oggetti

a) Le classi

Nel voler definire le classi partirò dalla filosofia, per poi spiegare cosa sono le classi in Java, in un secondo momento. Partiamo ora dal capitolo sulle classi dei Principi della matematica di Bertrand Russell: il capitolo ottavo.

Qui troviamo una buona spiegazione di cosa sono le classi:

«Quando un oggetto è denotato senza ambiguità da un concetto, diremo che questo concetto è un concetto (o, qualche volta, senz'altro il concetto) dell’oggetto in questione. Così sarà necessario distinguere il concetto di una classe dal concetto-classe, ma uomo, nel suo uso comune, non denota nulla.

Invece uomini e tutti gli uomini (che io considererò come sinonimi) denotano qualcosa, e io sosterrò che quanto essi denotano è la classe composta da tutti gli uomini. Ne segue che uomo è il concetto-classe, uomini (come concetto) è il concetto della classe, e gli uomini (l’oggetto denotato dal concetto uomini) sono la classe.»

(Russell, Bertrand, I principi della matematica, Bollati Boringhieri, Torino, 2011, p.120)

In questa definizione di classe ci accorgiamo subito che Russell distingue due concetti di classe:

il concetto-classe e la classe come collezione di individui.

Secondo Russell la matematica ha inteso sempre la classe come insieme nel senso estensionale, dunque come collezione degli individui che essa comprende. La classe dei pinguini, ad esempio, non sarebbe altro che l’insieme la cui estensione è definita dal numero dei suoi oggetti, ossia i pinguini.

Questo ha portato a pensare la classe nella maniera semplice della logica predicativa, nella quale si dice che un soggetto ha un predicato, se il soggetto come costante individuale appartiene alla classe-predicato.

In questo senso la classe la troviamo nelle proposizioni come “quella macchina è gialla”, con la quale intendiamo dire che quella macchina appartiene alla classe delle cose gialle.

Secondo Russell, invece, la classe non va definita solamente in termini di estensione, altrimenti non si potrebbe rendere conto delle classi infinite, come le classi dei numeri. Bisogna riconoscere anche un carattere di intensione della classe, quando la classe è definita come concetto classe.

“Uomo”, spiega Russell, non è che un concetto-classe.

Solamente gli uomini sono delle vere istanze o particolari. Questi ultimi sono i particolari della collezione degli uomini, essi sono : gli oggetti.

Dunque la filosofia tende prevalentemente a considerare le classi come insiemi definiti da una estensione, che è data dagli oggetti che compongono la classe. Si limita semplicemente ad aggiungere che la classe non è definita solo in termini estensionali. Questo è evidente perché non avremo mai una definizione di uomo, se ci limitiamo ad elencare gli oggetti che compongono quella data classe.

Ma cosa si intende per classe in Java?

Una classe è un modello che definisce proprietà e metodi di un oggetto. In Java la classe è un modello. In logica i modelli sono degli strumenti che ci permettono di definire la semantica di un linguaggio logico. Grazie ad un modello in logica ogni formula ha un significato preciso e fissato.

Nella logica predicativa, ad esempio, il modello definisce i predicati come insiemi specifici nel modello e le costanti individuali come istanze specifiche nel modello, le quali possono più o meno appartenere ad una classe data.

In Java la classe è un modello che definisce proprietà e metodi dell’oggetto, dunque che dichiara quali sono le proprietà di tutti gli oggetti che appartengono a quella classe e quali sono i comportamenti di tutti quegli oggetti. Esistono molti tipi di classi:

1) Le classi eseguibili

Normalmente in un programma abbiamo una classe eseguibile con un metodo principale. Solamente da questa classe è possibile eseguire l’intero codice del programma. Sicuramente avrete già visto che quando installate un programma esiste un file (file .exe, di solito), che è il file dal quale far partire l’intero programma e installarlo.

2) Le classi modello

Una classe modello è la classe in cui scriviamo proprietà e metodi degli oggetti. Questa classe spesso ha un costruttore, o più di uno, il quale ci permette di costruire gli oggetti.

3) Le classi astratte

Una classe astratta è una classe che non può costruire oggetti. Questa classe contiene proprietà e metodi, anche metodi astratti, i quali possono essere ereditati da altre classi modello. È facile pensare che se la classe Scoiattolo è una classe modello con la quale possiamo creare scoiattoli, una classe Roditore è una classe astratta che contiene solo proprietà e metodi che gli scoiattoli, in quanto roditori, ereditano da quella classe. Tuttavia non vi sono oggetti roditori.

4) Le classi formali e classi concrete

Un oggetto avrà sempre una classe formale che è dichiarata a sinistra e una classe concreta che è dichiarata a destra. Capiremo più avanti che, per via del polimorfismo, una classe formale di un oggetto potrebbe non essere identica alla classe concreta.

Questo è tutto sulle classi, anche perché la parte più corposa dell’argomento è data dagli oggetti, che sono l’elemento essenziale della programmazione orientata agli oggetti.

Qui troviamo l’ontologia di Java.

Ma prima di arrivare a parlare di oggetti vorrei farvi notare questo: un linguaggio basato su tipi e classi è necessariamente un linguaggio che ha un background fortemente platonico e è non un caso che io fino ad ora abbia citato molto Russell, perché Russell è un filosofo platonico.

Vediamo ora il tema degli oggetti. Per la spiegazione degli oggetti intendo partire dal capitolo sulle categorie dell’Organon di Aristotele, affinché si possa apprezzare l’analogia tra gli oggetti in filosofia e gli oggetti in informatica.

Il primo capitolo dell’Organon di Aristotele è il capitolo sulle categorie.

In questo capitolo Aristotele compone un’analogia tra la struttura dell’enunciato e quella della realtà. L’enunciato che hanno sempre studiato i filosofi è l’enunciato della forma “soggetto-predicato”, solo più avanti, prima con De Morgan e Peirce, successivamente con Russell, si è incominciato a porre il problema degli enunciati di relazione. L’enunciato soggetto/predicato è composto da un soggetto a cui viene predicato qualcosa.

Aristotele chiama questo “connessione”, ossia la predicazione ad un soggetto di qualcosa. Nel mondo reale il soggetto è una sostanza, mentre il predicato è una proprietà. La sostanza è l’oggetto in senso vero e proprio, il quale possiede una molteplicità di predicati. I tipi di predicati sono definiti dalle categorie di cui la sostanza stessa fa parte.

Secondo Aristotele esistono dieci categorie:

  • sostanza, qualità, quantità, agire, patire, luogo, tempo, situazione, relazione.

Questo è il modo in cui il pensiero classico della filosofia, la metafisica, concepisce l’oggetto. Le categorie di Aristotele ci possono sembrare molto distanti dal modello di Java, ma non lo sono affatto. In realtà i tipi funzionano quasi come le categorie.

Aristotele avrebbe detto secondo la categoria della quantità che un paio di scarpe ha la proprietà di essere 2 scarpe. In Java questo si esprime assegnando prima un tipo interno (quantità) ad una variabile (numero scarpe), inizializzata con il numero 2.

In Java un oggetto ha un’identità, delle proprietà e dei comportamenti.

Inoltre ogni oggetto appartiene sempre ad una classe. Nel caso non ne avesse una, sarebbe comunque della classe Object (oggetto). Questo lo si capisce bene in Aristotele quando parla della relazione tra la sostanza e la specie.

Socrate è un uomo, tradotto in Java, vuol dire che un dato oggetto, che ha come proprietà di chiamarsi Socrate, appartiene alla classe degli uomini.

In Aristotele non vediamo, tuttavia, il comportamento dell’oggetto trattato come qualcosa di distinto dalle proprietà, infatti Aristotele lo comprende nelle categorie dell’agire e del patire. Questo chiaramente vuol dire solo che l’ontologia di Java presenta differenze rispetto a quella di Aristotele.

In filosofia, come ci sono persone che non distinguerebbero le proprietà dai comportamenti, vi sono altre che direbbero che le proprietà sono dei comportamenti.

Levi Bryant, ad esempio, sostiene che il blu di una sfera non è qualcosa che la sfera possiede, ma un modo in cui la sfera agisce. In Java la nozione di oggetto è particolarmente estesa e si applica anche ai soggetti.

Il migliore corrispettivo della programmazione orientata all'oggetto in filosofia è l’ontologia orientata all'oggetto. Per conoscere meglio questa branca della filosofia continentale contemporanea vi consiglio di leggere il libro: Object oriented ontology: a new theory of everything, libro scritto da Graham Harman.

L’ontologia orientata all'oggetto intende difendere l’esistenza degli oggetti da ogni forma di riduzionismo. L’oggetto non va ridotto alle sue semplici proprietà, ma è sempre lo scarto tra la sostanza e le qualità. L’oggetto non è mai solo il processo da cui è nato. L’oggetto non è solamente la somma delle sue parti o riducibile alle sue parti.

In Java un oggetto è sempre di una classe, ma quando viene definito ha una classe formale e una concreta, le quali, come vedremo, in certi casi non coincidono.

Per esempio:

Uomo socrate = new Uomo();

Per convenzione il nome della classe è maiuscolo, mentre quello dell’oggetto, essendo l’oggetto in realtà una variabile, è sempre minuscolo, almeno per la prima lettera. Uomo a sinistra è la classe formale di socrate, mentre quella scritta dopo la keyword “new” è la classe concreta. Un oggetto inoltre ha delle proprietà e dei metodi. Le proprietà di un oggetto sono delle variabili.

Il nostro oggetto, ad esempio, potrebbe avere le seguenti proprietà:

String nome = “Socrate”;
String residenza = “Atene”;
String professione = “Filosofo”;
int eta = 30;
double risparmi = 2000.55;
boolean saggio = true;

Ogni proprietà ha un tipo a seconda della proprietà. Il nome, la residenza, la professione sono delle stringhe. L’età è un intero, mentre i soldi risparmiati sono un double. Settando la proprietà “saggio” come true si sta dicendo che Socrate è un saggio.

Oltre alle proprietà un oggetto ha anche dei comportamenti che sono definiti dai metodi. I metodi sono i comportamenti degli oggetti. A partire da Spinoza si dice in filosofia che un oggetto ha la capacità di produrre affetti su altri oggetti o quella di subirla.

Se prendiamo un oggetto semplice come una penna, vediamo che la sua capacità è quella di scrivere, nel senso di produrre affetti su certi oggetti come la carta. Tuttavia la penna può essere impugnata e mossa da una mano umana, ossia subire degli affetti. Queste operazioni rientrano nelle categorie aristoteliche dell’agire e del patire.

Se prendiamo una penna come oggetto potremmo definire in Java un metodo di questo tipo:

esempio java  10
esempio java 10

Vediamo che questo metodo è pubblico, ma cosa sia “pubblico” lo spiegherò più avanti. Vediamo inoltre che di tipo booleano.

Il nome del metodo è “penna”.

Tra le parentesi tonde troviamo un parametro. Questo parametro è un oggetto di tipo Penna che noi passiamo al metodo. All'inizio impostiamo la risposta come falsa.

Con questo intendiamo che di default la penna non scrive. Successivamente verifichiamo che la penna abbia dell’inchiostro. “getInchiostro()” è un metodo getter che ci serve per prendere il valore di una proprietà booleana (booelan inchistro) dalla classe Penna. Se la penna ha l’inchiostro, allora vuol dire che scrive. Per ultima mossa mettiamo un “return” per restituire il valore della nostra risposta.

Vediamo ora come possiamo costruire un intero sistema che ci permette di creare degli oggetti.

Per fare questo bisogna creare un programma con delle classi modello che definiscono delle entità di un certo tipo e una classe eseguibile che esegue il codice e nella quale sono definiti i valori di quelle entità.

La mia intenzione è di costruire delle persone. Per fare questo dobbiamo definire una classe modello Persona, definire le proprietà e i metodi di questa classe, nonché aggiungere un costruttore. Un costruttore è un metodo particolare che ha come parametri tutte le proprietà dell’oggetto e che serve per creare degli oggetti.

Richiamando il costruttore nella classe eseguibile, ossia quella con il metodo principale, possiamo generare tutti gli uomini che vogliamo, i quali avranno per proprietà e metodi quelli che abbiamo definito nella classe modello.

Questa è la classe modello:

package entities;

public class Persona {
	
	private String nome;
	
	private String professione;
	
	private String residenza;
	
	private int eta;
	
	private double stipendio;

	public Persona(String nome, String professione, 
String residenza, int eta, double stipendio) {
		super();
		this.nome = nome;
		this.professione = professione;
		this.residenza = residenza;
		this.eta = eta;
		this.stipendio = stipendio;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getProfessione() {
		return professione;
	}

	public void setProfessione(String professione) {
		this.professione = professione;
	}

	public String getResidenza() {
		return residenza;
	}

	public void setResidenza(String residenza) {
		this.residenza = residenza;
	}

	public int getEta() {
		return eta;
	}

	public void setEta(int eta) {
		this.eta = eta;
	}

	public double getStipendio() {
		return stipendio;
	}

	public void setStipendio(double stipendio) {
		this.stipendio = stipendio;
	}
	
	public boolean isSaggio() {
		
		boolean risposta = false;
		
		switch(professione) {
		
		case "filosofo":
			
			risposta = true;
		
		break;
		
		case "matematico":
			
			risposta = true;
			
			break;
			
		default:
			
			risposta = false;
		
		
		}
		
		
		return risposta;
	}
	
	


public boolean isVecchio() {
		
		boolean risposta = false;
		
		if(eta > 50) {
			
			risposta = true;
		}
		
		return risposta;
	}

}

Questa è la classe eseguibile con le nostre persone:

package controller;

import entities.Persona;

public class EsecutorediPersone {

	public static void main(String[] args) {
		
		Persona socrate = new Persona("Socrate", "filosofo", "Atene", 30, 1200);
		Persona russell = new Persona("Bertrand Russell", "filosofo", "Oxford", 50, 3200);
		Persona riemann = new Persona("Bernard Riemann", "matematico", "Gottinga", 40, 2200);
		Persona peterPan = new Persona("Peter Pan", "nullafacente", "L'isola che non c'è", 18, 1200);
		
	}

}

Il nostro progetto si chiama “Generatore di Persone”. Nella nostra cartella progetto di Eclipse troviamo JRE e la nostra cartella sorgente.

Nella cartella sorgente abbiamo due package:

  • entities per le entità;
  • controller per il main.

Nel primo package abbiamo la nostra classe modello: Persona.java. Nel secondo package abbiamo la nostra classe eseguibile: EsecutoreDiPersone.java. Nella prima classe definiamo le proprietà (nome, professione, residenza, eta, stipendio), inseriamo un costruttore, inseriamo dei getter and setter per poter prendere e modificare le nostre proprietà, definiamo dei metodi (isSaggio, isVecchio). I nostri due metodi sono dei booleani che verificano per ogni oggetto se è saggio e se è vecchio. Nella classe eseguibile, invece, creiamo i nostri oggetti assegnando loro i valori delle loro proprietà.

b) Gli Array e gli ArrayList

Prima ancora di vedere quali sono i tre principi fondamentali della programmazione orientata agli oggetti, è meglio spostare la nostra attenzione su un altro tema: gli Array e gli ArrayList. Un Array è semplicemente una variabile che contiene più valori dello stesso tipo, che ha un indice fisso e che può avere più di una dimensione. Vediamo un esempio pratico:

String animali[ ] = new String[4]

String[0] = “delfino”;

String[1] = “gatto”;

String[2] = “capra”;

String[3] = “gallina”;

Ogni Array deve avere un tipo (es. String), come tutte le variabili. Tutti gli elementi dell’Array saranno necessariamente di quel tipo. L’Array come le variabili ha sempre un nome e deve essere inizializzato. Prima di inizializzare l’Array bisogna indicare il numero che definisce il suo indice.

Questo numero segna il numero degli elementi dell’Array. Bisogna stare attenti al fatto che il numero degli elementi non coincide mai con il numero dell’elemento. Infatti, quando contiamo gli elementi di un Array partiamo sempre da 0 e mai da 1. Quindi, se in un Array ci sono 4 elementi, l’ultimo elemento dell’Array non sarà il numero 4, ma il numero 3. Lo si vede chiaramente dall'esempio sugli animali che ho fatto prima. Possiamo anche costruire degli Array multidimensionali, è sufficiente aggiungere delle parentesi quadre. Ad esempio:

int numeri[ ][ ] = {{3,5,7},{2,6,9},{9,7,5}};

Se voglio ottenere una lista degli elementi degli Array non mi resta che usare il ciclo for e ciclarli in questo modo:

for(int i = 0; i < animali.length; i++){

     System.out.println(“Animale ” + i +”: ” + animali[i]);
     
}

Gli Array hanno il difetto che hanno un indice fisso in Java, dunque non si possono aggiungere nuovi elementi o rimuoverne di già esistenti. Da questo punto di vista appaiono migliori gli ArrayList. Un ArrayList è un Array di soli oggetti, il quale può subire delle modifiche, in quanto possiamo aggiungere o eliminare oggetti dalla lista. Un esempio:

ArrayList<Animale> animale = new ArrayList<>();
Animale tom = new Animale (“gatto”, 20, “grigio”, true);
animale.add(tom);

Usando la funzione “.add()” possiamo aggiungere oggetti alla lista, mentre se usiamo la funzione “.remove()”, possiamo togliere oggetti dalla lista.

c) I tre principi della programmazione orientata agli oggetti

I tre principi della programmazione orientata agli oggetti sono:

l’ereditarietà, il polimorfismo e l’incapsulamento. L’ereditarietà significa che una certa classe eredita le proprietà e i metodi di un’altra classe se estende quella classe. Il polimorfismo è la capacità di del codice di assumere più forme, per esempio di una classe di avere una classe formale diversa da quella concreta. L’incapsulamento consiste nella strategia del nascondere l’informazione e limitarne l’accessibilità.

Questa è ovviamente una spiegazione molto veloce, vedremo meglio tutti questi elementi, ma per spiegarvi come funzionano intendo partire dall'ontologia dei filosofi. Secondo una vecchia nozione di ontologia, l’ontologia è la classificazione di tutto l’essere. Un modello ben definito per fare tutto questo è quello di Aristotele: la dialettica.

Aristotele crea un modello a tre livelli: genere, specie, individuo. L’animale è il genere dell’uomo, in quanto tutti gli uomini sono animali. Uomo è una specie di animale, come ne esistono molte altre: tigri, pavoni, pantere, ecc. Socrate è un individuo, un singolo uomo. In Java tutto questo si può esprimere in questo modo: Animale è la classe padre (genere) della classe Uomo, che è la classe figlio (specie), mentre Socrate è l’oggetto (individuo) della classe modello Uomo.

L’ontologia si costruisce con il metodo della divisione che consiste nell’or esclusivo. Per esempio posso dividere il genere animale in due specie: animali razionali e animali non razionali.

In Java avremmo solamente una classe padre e due classi figlio.

L’ereditarietà di Java funziona proprio in questo modo: la classe figlio (specie) eredità proprietà e metodi dalla classe padre (genere). In termini filosofici l’ereditarietà significa questo: che tutti gli uomini hanno certamente tutte quelle proprietà e comportamenti che contraddistinguono l’animale, essendo tutti gli uomini animali.

In tempi molto più recenti un filosofo analitico ha definito tutte le regole fondamentali per costruire un modello ontologico in maniera rigorosa. Questo filosofo si chiama: Roderick Chisholm.

Le regole sono tre:

Filosofo Roderick Chisholm
Roderick Chisholm

1) Il modello ontologico deve essere rappresentano come albero in senso matematico, ossia un particolare grafo ad albero. Ha dei nodi, ci sono dei livelli gerarchici, ma non ci sono cicli (loop), nel senso che non si risale dai piani bassi a quelli alti.

2) Devono esistere dei nodi minimali che non rimandano ad altre categorie o ulteriori specie. In questo modo l’albero come grafo ha un termine.

3) Esiste un solo nodo alto e massimale, per questo non vi sono rombi. Non è possibile che una specie stia sotto generi diversi.

La prima regola definisce come è graficamente il modello ontologico e la sua struttura. La seconda ci impone che il modello ontologico termini e non si divida all'infinito. La terza ci impone che il modello ontologico abbia sempre un solo nodo di genere per più nodi di specie (non esistono rombi).

In Java questo si traduce in questo modo:

  • dobbiamo definire la classe padre e la classe figlio come classi in una relazione gerarchica di ereditarietà, la quale sembra creare proprio una struttura ad albero che in programmazione non vediamo;
  • le classi chiaramente non sono infinite, dunque se mettiamo le classi padre come classi astratte, necessariamente ci saranno delle classi figlio concrete con le quali termina tutto il modello;
  • esiste una sola classe padre per ogni classe figlio, non esistono più classi padre per la stessa classe figlio (non esistono rombi).

Per spiegare meglio tutti i principi della programmazione orientata agli oggetti e altri concetti nuovi come quello di classe astratta, intendo costruire un piccolo gioco fantasy con Java, di modo da poter apprezzare tutte le funzioni di questo linguaggio.

Comincerò, tuttavia, ancora prima di programmare il programma passo a passo, a definire un modello ontologico, come si fa in filosofia, delle classi del pacchetto entità che userò per il mio gioco.

Il Gioco: Java spiegato ai filosofi
Il Gioco: Java spiegato ai filosofi

Così vedremo direttamente il gioco scritto in Java discendere da un procedimento filosofico.

Questo è il modello ontologico del mio gioco:

Dunque abbiamo il genere guerriero come genere assoluto, poiché tutti i personaggi del gioco saranno dei guerrieri. Esistono due specie di guerrieri: i mostri e gli umanoidi. Di mostri ne esistono di tre tipi: vampiri, orchi e troll. Di umanoidi ne esistono anche di tre tipi: nani, elfi e umani.

Il gioco è un gioco fantasy di battaglia, nel quale noi possiamo scegliere la nostra razza e combattere contro un personaggio di un’altra razza. In Java il modello si traduce in questo modo: abbiamo prima una classe astratta “Guerriero”; poi ci sono altre due classi astratte “Umanoide” e “Mostro” che ereditano proprietà e metodi dalla classe “Guerriero”; infine abbiamo tre classi concrete per “Umanoide” e tre classi concrete per “Mostro” che ereditano proprietà e metodi dalla classe padre.

Apriamo il programma di Eclipse, creiamo un progetto java.

Nella cartella sorgente del progetto incominciamo a inserire un package entities per le entità. Creiamo per prima cosa una classe astratta

Guerriero con proprietà e metodi:

package entities;

public abstract class Guerriero {
	
	private int vita;
	
	private int attacco;
	
	private int difesa;
	
	private int velocita;
	
	private int agilita;
	
	
	


	public Guerriero(int vita, int attacco, int difesa, int velocita, int agilita) {
		super();
		this.vita = vita;
		this.attacco = attacco;
		this.difesa = difesa;
		this.velocita = velocita;
		this.agilita = agilita;
	}
	
	
	

	public int getVita() {
		return vita;
	}




	public void setVita(int vita) {
		this.vita = vita;
	}




	public int getAttacco() {
		return attacco;
	}




	public void setAttacco(int attacco) {
		this.attacco = attacco;
	}




	public int getDifesa() {
		return difesa;
	}




	public void setDifesa(int difesa) {
		this.difesa = difesa;
	}




	public int getVelocita() {
		return velocita;
	}




	public void setVelocita(int velocita) {
		this.velocita = velocita;
	}




	public int getAgilita() {
		return agilita;
	}




	public void setAgilita(int agilita) {
		this.agilita = agilita;
	}

    


	@Override
	public String toString() {
		return "Guerriero [vita=" + vita + ", attacco=" + attacco + ", difesa=" + difesa + ", velocita=" + velocita
				+ ", agilita=" + agilita + "]";
	}




	public int tuttaDifesa() {
		
		int tuttaDif = difesa + 5;
		
		
		return tuttaDif;
	}
	
	public int soloAttacco() {
		
		int soloAtt = attacco + 5;
		
		return soloAtt;
		
	}

Una classe astratta è una classe dalla quale non si possono costruire degli oggetti.

Una classe astratta può avere dei metodi e delle proprietà. Inoltre la classe astratta può avere un particolare tipo di metodo che è il metodo astratto.

Come proprietà abbiamo cinque variabili di tipo integer: attacco, difesa, velocità, agilità e vita. Queste sono le proprietà base e generali di ogni guerriero, a prescindere dalla sua razza. Noterete che queste proprietà sono “private”, ossia sono private.

Il terzo principio della programmazione orientata agli oggetti è l’incapsulamento.

Si tratta di una pratica per nascondere l’informazione. Quando mettiamo una variabile o un metodo come privato intendiamo che quella proprietà o quel metodo sarà solo accessibile all'interno di quella classe.

Possiamo invece mettere la variabili anche come protected o public.

Se la mettiamo come “protected” la variabile sarà solo accessibile nella classe dello stesso package o alle classi figlio di quella classe.

Quando invece viene indicata come “public”, ossia pubblica, la variabile sarà accessibile a tutte le classi.

Dal momento che intendiamo mettere queste variabili come private, per accedere a queste variabili da altre classi aggiungiamo dei getter e dei setter, di modo da poter prendere le variabili o assegnargli un valore, anche da altre classi.

In questa classe astratta ho inserito anche due metodi pubblici, uno per la difesa e l’altro per l’attacco.

L’idea è che il guerriero che attacca si dispone per l’attacco, mentre quello che difende si dispone per la difesa. Questi due metodi semplicemente aggiungono 5 all'attacco o alla difesa, a seconda della tipologia del metodo e a seconda se il guerriero stia attaccando o difendendo.

Guerriero è il genere assoluto, dal momento che si immagina che in questo gioco fantasy non vi siano altro che guerrieri. È una classe astratta perché, sebbene tutti gli esseri del gioco sono guerrieri, nessuno è semplicemente un’istanza di Guerriero, poiché ogni guerriero sarà sempre un certo tipo di guerriero di una certa specie.

Tutti gli esseri, comunque, avranno sempre un attacco, una difesa, una velocità, una agilità e una vita. Ovviamente per ogni guerriero, ossia per ogni oggetto, cambieranno i valori di queste proprietà. Ad esempio un elfo è più agile del troll, un orco più forte del nano, ecc.

Il nostro grafo dopo Guerriero si dirama in due classi: Mostro e Umanoide. Con questo intendo dire che esistono due specie di Guerriero: o Mostro o Umanoide.

Per fare questo creiamo altre due classi astratte nel package entities:

una classe Mostro e una classe Umanoide.

Questa è la classe Mostro che ho scritto:

package entities;

public abstract class Mostro extends Guerriero{

	
	private boolean sangueFreddo;
	
	private int robustezza;
	
	private int pelleDura;
	
	
	
	




	public Mostro(int vita, int attacco, int difesa, int velocita, int agilita, boolean sangueFreddo, int robustezza,
			int pelleDura) {
		super(vita, attacco, difesa, velocita, agilita);
		this.sangueFreddo = sangueFreddo;
		this.robustezza = robustezza;
		this.pelleDura = pelleDura;
	}


     





	@Override
	public String toString() {
		return "Mostro [sangueFreddo=" + sangueFreddo + ", robustezza=" + robustezza + ", pelleDura=" + pelleDura + "]";
	}








	public boolean isSangueFreddo() {
		return sangueFreddo;
	}








	public void setSangueFreddo(boolean sangueFreddo) {
		this.sangueFreddo = sangueFreddo;
	}








	public int getRobustezza() {
		return robustezza;
	}








	public void setRobustezza(int robustezza) {
		this.robustezza = robustezza;
	}








	public int getPelleDura() {
		return pelleDura;
	}








	public void setPelleDura(int pelleDura) {
		this.pelleDura = pelleDura;
	}








	public int travolgere() {
		
		int res = 0;
		
		if(sangueFreddo) {
			
			res = 10;
		}
		
		
		return res;
	}
	
}




}

In questa classe, come in tutte le altre, troviamo delle proprietà e dei metodi. Ci sono due proprietà intere che sono la “robustezza” e la “pelle dura”. Queste proprietà potenziano la difesa del mostro, essendo il mostro una creatura robusta, resistente e dalla pelle spessa.

La terza proprietà, la proprietà “sangue freddo”, è un booleano. Questa proprietà è associata al metodo “travolgere”.

Se il valore del booleano è “true”, allora il metodo travolgere permetterà al mostro, quando attacca, di avere un attacco potenziato.

Anche questa classe Mostro è astratta, infatti vi sono istanze di mostro, non ci sono oggetti mostro che non siano già un certo tipo di mostro, ossia gli oggetti devono avere comunque come classe concreta una classe diversa dalla classe formale Mostro.

Avrete notato in alto il termine “extends Guerriero”.

Questa espressione sta per il primo principio della programmazione orientata agli oggetti: l’ereditarietà.

In questo senso il mostro eredità le proprietà e i metodi dalla classe guerriero, dunque ogni mostro ha un attacco, una velocità, una difesa, ecc. Anche qui le proprietà sono private e i metodi sono pubblici.

Questa è invece la classe astratta Umanoide:

package entities;

public abstract class Umanoide extends Guerriero{
	
	private int scioltezza;
	
	private boolean piedeAchille;
	
	private int ingenieristica;


	
	
	
	public Umanoide(int vita, int attacco, int difesa, int velocita, int agilita, int scioltezza, boolean piedeAchille,
			int ingenieristica) {
		super(vita, attacco, difesa, velocita, agilita);
		this.scioltezza = scioltezza;
		this.piedeAchille = piedeAchille;
		this.ingenieristica = ingenieristica;
	}

    



	public int getScioltezza() {
		return scioltezza;
	}





	public void setScioltezza(int scioltezza) {
		this.scioltezza = scioltezza;
	}





	public boolean isPiedeAchille() {
		return piedeAchille;
	}





	public void setPiedeAchille(boolean piedeAchille) {
		this.piedeAchille = piedeAchille;
	}





	public int getIngenieristica() {
		return ingenieristica;
	}





	public void setIngenieristica(int ingenieristica) {
		this.ingenieristica = ingenieristica;
	}

    



	@Override
	public String toString() {
		return "Umanoide [scioltezza=" + scioltezza + ", piedeAchille=" + piedeAchille + ", ingenieristica="
				+ ingenieristica + "]";
	}





	public int isVelocissimo() {
		
		int res = 0;
		
		if(piedeAchille) {
			
			res = 10;
		}
		
		
		return res;
	}

}

Umanoide è una specie di guerriero, dunque estende Guerriero. Guerriero è la classe padre di Umanoide e Umanoide è la classe figlio. La classe Umanoide eredita dalla classe Guerriero proprietà e metodi. Anche la classe Umanoide è astratta. Questa classe è una classe modello per tutti quegli enti che sono umanoidi.

In questa classe vengono definite le proprietà e i metodi particolari di quegli enti. Come proprietà abbiamo la scioltezza, il piede di Achille e l’ingegneristica. La scioltezza lavora sull’agilità, l’ingegneristica è invece la capacità di fabbricare armi e aumenta l’attacco.

A questo punto si creano le classi delle varie creature.

Prime le classi delle specie di mostro e successivamente le classi delle specie di umanoide. Le specie di mostro sono tre: i troll, gli orchi e i vampiri.

Le specie di umanoidi sono anche tre: gli elfi, gli uomini e i nani.

Cominciamo con la classe dei Troll:

package entities;

import java.util.Random;

public class Troll extends Mostro{
	
	private String nome;
	
	private String specie;
	
	private String tribu;
	
	private int pesantezza;
	
	private boolean resistenzaAccanita;
	
	private int devastazione;

	public Troll(int vita, int attacco, int difesa, int velocita, int agilita, boolean sangueFreddo, int robustezza,
			int pelleDura, String nome, String specie, String tribu, int pesantezza, boolean resistenzaAccanita,
			int devastazione) {
		super(vita, attacco, difesa, velocita, agilita, sangueFreddo, robustezza, pelleDura);
		this.nome = nome;
		this.specie = specie;
		this.tribu = tribu;
		this.pesantezza = pesantezza;
		this.resistenzaAccanita = resistenzaAccanita;
		this.devastazione = devastazione;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getSpecie() {
		return specie;
	}

	public void setSpecie(String specie) {
		this.specie = specie;
	}

	public String getTribu() {
		return tribu;
	}

	public void setTribu(String tribu) {
		this.tribu = tribu;
	}

	public int getPesantezza() {
		return pesantezza;
	}

	public void setPesantezza(int pesantezza) {
		this.pesantezza = pesantezza;
	}

	public boolean isResistenzaAccanita() {
		return resistenzaAccanita;
	}

	public void setResistenzaAccanita(boolean resistenzaAccanita) {
		this.resistenzaAccanita = resistenzaAccanita;
	}

	public int getDevastazione() {
		return devastazione;
	}

	public void setDevastazione(int devastazione) {
		this.devastazione = devastazione;
	}

	@Override
	public String toString() {
		return "Troll [nome=" + nome + ", specie=" + specie + ", tribu=" + tribu + ", pesantezza=" + pesantezza
				+ ", resistenzaAccanita=" + resistenzaAccanita + ", devastazione=" + devastazione + "]";
	}
	
	
	public int invincibile() {
		
		int res = 0;
		 
		if(resistenzaAccanita) {
			
			res = 50;
		}
		 
		
		return res;
	}
	

	public int Arma() {

		int res = 0;
		
		String arma = "";
		
		Random dice = new Random();
		
		int numero = dice.nextInt(3)+1;
		
		switch(numero) {
		
		case 1:
			
		arma = "clava";
		
		break;
		
		case 2: 
			
		arma = "schiacciateste";
		
		break;
		
		case 3:
			
		arma = "mazza";
		
		break;
		
		
		}
		
		
		switch(arma) {
		
		case "clava":
			
			res = 8;
			
			break;
			
		case "schiacciateste":
			
			res = 10;
			
			break;
			
		case "mazza":
			
			res = 15;
			
			break;
		
		
		}
		

		return res;
	}

	public int attaccoTroll() {
		int res = 0;

		return res = getAttacco() + soloAttacco() + travolgere() + getDevastazione()
				+ Arma();
	}

	public int difesaTroll() {

		int res = 0;

		return res = getDifesa() + tuttaDifesa() + getPelleDura() + getRobustezza()
				+ getPesantezza();

	}

	public int velocitaTroll() {

		int res = 0;

		return res = getVelocita();

	}

	public int agilitaTroll() {

		int res = 0;

		return res = getAgilita();
	}

	public int aumentovitaTroll() {

		int res = 0;

		return res = invincibile();
	}

}

In questa classe definiamo le proprietà e i metodi della classe Troll.

Il troll è una specie di mostro, dunque Troll è una classe figlio della classe padre Mostro.

In accordo con il principio dell’ereditarietà il Troll ha tutte le proprietà sia del guerriero, che del mostro. Inoltre il Troll possiede delle proprietà che lo contraddistinguono come troll.

Ogni troll ha un nome, appartiene ad una specie specifica di troll (es. troll di roccia), fa parte di un clan (es. troll della montagna), è pesante e ha la capacità di travolgere. Inoltre un valore booleano che, se è inizializzato come true, allora fa sì che il metodo invincibile() aumenti di 50 la sua vita.

Il metodo dell’arma, invece, funziona in questo modo: ho creato un dato (Random dice) che, una volta lanciato, da un numero e sulla base di quel numero viene assegnata una delle tre armi presenti tra le opzioni al troll.

Il secondo mostro è l’orco:

package entities;

import java.util.Random;

public class Orco extends Mostro{
	
	private String nome;
	
	private String specie;
	
	private String clan;
	
	private int forzaBrutale;
	
	private boolean effettoOrda;
	
	private int energiaCaos;

	public Orco(int vita, int attacco, int difesa, int velocita, int agilita, boolean sangueFreddo, int robustezza,
			int pelleDura, String nome, String specie, String clan, int forzaBrutale, boolean effettoOrda,
			int energiaCaos) {
		super(vita, attacco, difesa, velocita, agilita, sangueFreddo, robustezza, pelleDura);
		this.nome = nome;
		this.specie = specie;
		this.clan = clan;
		this.forzaBrutale = forzaBrutale;
		this.effettoOrda = effettoOrda;
		this.energiaCaos = energiaCaos;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getSpecie() {
		return specie;
	}

	public void setSpecie(String specie) {
		this.specie = specie;
	}

	public String getClan() {
		return clan;
	}

	public void setClan(String clan) {
		this.clan = clan;
	}

	public int getForzaBrutale() {
		return forzaBrutale;
	}

	public void setForzaBrutale(int forzaBrutale) {
		this.forzaBrutale = forzaBrutale;
	}

	public boolean isEffettoOrda() {
		return effettoOrda;
	}

	public void setEffettoOrda(boolean effettoOrda) {
		this.effettoOrda = effettoOrda;
	}

	public int getEnergiaCaos() {
		return energiaCaos;
	}

	public void setEnergiaCaos(int energiaCaos) {
		this.energiaCaos = energiaCaos;
	}

	@Override
	public String toString() {
		return "Orco [nome=" + nome + ", specie=" + specie + ", clan=" + clan + ", forzaBrutale=" + forzaBrutale
				+ ", effettoOrda=" + effettoOrda + ", energiaCaos=" + energiaCaos + "]";
	}
	
	
	public int aumentoVitalita() {
		
		int res = 0;
		
		if(effettoOrda) {
			
			res = 50;
		}
		
		
		return res;
	}
	
	
	public int Arma() {

		int res = 0;
		
		String arma = "";
		
		Random dice = new Random();
		
		int numero = dice.nextInt(3)+1;
		
		switch(numero) {
		
		case 1:
			
		arma = "ascia";
		
		break;
		
		case 2: 
			
		arma = "scure";
		
		break;
		
		case 3:
			
		arma = "martello";
		
		break;
		
		
		}
		
		
		switch(arma) {
		
		case "ascia":
			
			res = 8;
			
			break;
			
		case "scure":
			
			res = 10;
			
			break;
			
		case "martello":
			
			res = 15;
			
			break;
		
		
		}
		

		return res;
	}
	
	public int attaccoOrco() {

		int res = 0;

		return res = getAttacco() + soloAttacco() + travolgere() + getForzaBrutale() + Arma();
	}

	public int difesaOrco() {

		int res = 0;

		return res = getDifesa() + tuttaDifesa() + getPelleDura() + getRobustezza();

	}

	public int velocitaOrco() {

		int res = 0;

		return res = getVelocita();
	}

	public int agilitaOrco() {

		int res = 0;

		return res = getAgilita() + getEnergiaCaos();
	}

	public int aumentovitaOrco() {

		int res = 0;

		return res = aumentoVitalita();
	}


}

L’Orco è una specie di mostro. Oltre alle normali proprietà di tutti i mostri l’orco ha un nome, appartiene ad una specie particolare di orco ( es. orco nero), fa parte di un clan (es. teschio di fuoco), ha una forza brutale che gli aumenta l’attacco e una energia del caos che gli aumenta l’agilità.

Come il troll ha una variabile booleana che, quando viene settata come vera fa sì che un metodo dell’orco aumenti la vita dell’orco di 50.

Anche l’orco ha un metodo per l’arma che gli permette di scegliere l’arma random tra tre armi: ascia, scure, martello.

Vedete anche che al termine del codice, ma questo vale per tutti, anche il troll, ci sono dei metodi che permettono di calcolare l’attacco, la difesa, la velocità e l’agilità totali del mostro, di modo che potremmo usare successivamente questi metodi per calcolare le proprietà reali della creatura presa in questione.

L’ultimo dei mostri è il vampiro:

package entities;

import java.util.Random;

public class Vampiro extends Mostro {

	private String nome;
	
	private String specie;
	
	private String casata;
	
	private boolean seteDiSangue;

	private int dentiAffilati;

	private int potereOscuro;

	

	public Vampiro(int vita, int attacco, int difesa, int velocita, int agilita, boolean sangueFreddo, int robustezza,
			int pelleDura, String nome, String specie, String casata, boolean seteDiSangue, int dentiAffilati,
			int potereOscuro) {
		super(vita, attacco, difesa, velocita, agilita, sangueFreddo, robustezza, pelleDura);
		this.nome = nome;
		this.specie = specie;
		this.casata = casata;
		this.seteDiSangue = seteDiSangue;
		this.dentiAffilati = dentiAffilati;
		this.potereOscuro = potereOscuro;
	}
	
	
	

	@Override
	public String toString() {
		return "Vampiro [nome=" + nome + ", specie=" + specie + ", casata=" + casata + ", seteDiSangue=" + seteDiSangue
				+ ", dentiAffilati=" + dentiAffilati + ", potereOscuro=" + potereOscuro + "]";
	}




	public String getNome() {
		return nome;
	}




	public void setNome(String nome) {
		this.nome = nome;
	}




	public String getSpecie() {
		return specie;
	}




	public void setSpecie(String specie) {
		this.specie = specie;
	}




	public String getCasata() {
		return casata;
	}




	public void setCasata(String casata) {
		this.casata = casata;
	}




	public boolean isSeteDiSangue() {
		return seteDiSangue;
	}




	public void setSeteDiSangue(boolean seteDiSangue) {
		this.seteDiSangue = seteDiSangue;
	}




	public int getDentiAffilati() {
		return dentiAffilati;
	}




	public void setDentiAffilati(int dentiAffilati) {
		this.dentiAffilati = dentiAffilati;
	}




	public int getPotereOscuro() {
		return potereOscuro;
	}




	public void setPotereOscuro(int potereOscuro) {
		this.potereOscuro = potereOscuro;
	}




	public int energiaDelSangue() {

		int res = 0;

		if (seteDiSangue) {

			res = 50;
		}

		return res;
	}

	public int Arma() {

		int res = 0;
		
		String arma = "";
		
		Random dice = new Random();
		
		int numero = dice.nextInt(3)+1;
		
		switch(numero) {
		
		case 1:
			
		arma = "artiglio";
		
		break;
		
		case 2: 
			
		arma = "accetta stregata";
		
		break;
		
		case 3:
			
		arma = "spada oscura";
		
		break;
		
		
		}
		
		
		switch(arma) {
		
		case "artiglio":
			
			res = 8;
			
			break;
			
		case "accetta stregata":
			
			res = 10;
			
			break;
			
		case "spada oscura":
			
			res = 15;
			
			break;
		
		
		}
		

		return res;
	}
	
	public int attaccoVampiro() {

		int res = 0;

		return res = getAttacco() + soloAttacco() + travolgere() + getDentiAffilati() + Arma();

	}

	public int difesaVampiro() {

		int res = 0;

		return res = getDifesa() + tuttaDifesa() + getRobustezza() + getPelleDura();
	}

	public int velocitaVampiro() {

		int res = 0;

		return res = getVelocita();
	}

	public int agilitaVampiro() {

		int res = 0;

		return res = getAgilita() + getPotereOscuro();
	}

	public int aumentovitaVampiro() {

		int res = 0;

		return res = energiaDelSangue();
	}

}

Il vampiro possiede tutte le qualità del mostro, ma ha anche un nome, appartiene ad una specie vi vampiri (es. vampiro oscuro), appartiene ad una casata (es. i Drakkar), ha denti affilati e un potere oscuro.

Anche lui ha un metodo che gli aumenta la vita, un metodo per la scelta dell’arma da usare con tre opzioni, dei metodi che ricalcolano la velocità, l’agilità, l’attacco e la difesa.

Dopo i mostri vengono gli umanoidi.

Tra gli umanoidi troviamo prima di tutto l’elfo:

package entities;

import java.util.Random;

public class Elfo extends Umanoide{
	
	private String nome;
	
	private String specie;
	
	private String villaggio;
	
	private int arrampicatore;
	
	private boolean potereArcano;
	
	private int leggerezza;

	public Elfo(int vita, int attacco, int difesa, int velocita, int agilita, int scioltezza, boolean piedeAchille,
			int ingenieristica, String nome, String specie, String villaggio, int arrampicatore, boolean potereArcano,
			int leggerezza) {
		super(vita, attacco, difesa, velocita, agilita, scioltezza, piedeAchille, ingenieristica);
		this.nome = nome;
		this.specie = specie;
		this.villaggio = villaggio;
		this.arrampicatore = arrampicatore;
		this.potereArcano = potereArcano;
		this.leggerezza = leggerezza;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getSpecie() {
		return specie;
	}

	public void setSpecie(String specie) {
		this.specie = specie;
	}

	public String getVillaggio() {
		return villaggio;
	}

	public void setVillaggio(String villaggio) {
		this.villaggio = villaggio;
	}

	public int getArrampicatore() {
		return arrampicatore;
	}

	public void setArrampicatore(int arrampicatore) {
		this.arrampicatore = arrampicatore;
	}

	public boolean isPotereArcano() {
		return potereArcano;
	}

	public void setPotereArcano(boolean potereArcano) {
		this.potereArcano = potereArcano;
	}

	public int getLeggerezza() {
		return leggerezza;
	}

	public void setLeggerezza(int leggerezza) {
		this.leggerezza = leggerezza;
	}

	@Override
	public String toString() {
		return "Elfo [nome=" + nome + ", specie=" + specie + ", villaggio=" + villaggio + ", arrampicatore="
				+ arrampicatore + ", potereArcano=" + potereArcano + ", leggerezza=" + leggerezza + "]";
	}
	
	public int potereDellaNatura() {
		
		int res = 0;
		
		if(potereArcano) {
			
			res = 50;
		}
		
		
		return res;
	}
	
	public int Arma() {

		int res = 0;
		
		String arma = "";
		
		Random dice = new Random();
		
		int numero = dice.nextInt(3)+1;
		
		switch(numero) {
		
		case 1:
			
		arma = "arco";
		
		break;
		
		case 2: 
			
		arma = "doppio pugnale";
		
		break;
		
		case 3:
			
		arma = "cerbottana";
		
		break;
		
		
		}
		
		
		switch(arma) {
		
		case "arco":
			
			res = 8;
			
			break;
			
		case "doppio pugnale":
			
			res = 10;
			
			break;
			
		case "cerbottana":
			
			res = 15;
			
			break;
		
		
		}
		

		return res;
	}
	
	public int attaccoElfo() {

		int res = 0;

		return res = getAttacco() + soloAttacco() + getIngenieristica() + Arma();

	}
	
	public int difesaElfo() {

		int res = 0;

		return res = getDifesa() + tuttaDifesa() + getArrampicatore();

	}
	
	public int velocitaElfo() {

		int res = 0;

		return res = getVelocita() + isVelocissimo();
	}
	
	public int agilitaElfo() {

		int res = 0;

		return res = getAgilita() + getScioltezza() + getLeggerezza();
	}

	public int aumentovitaElfo() {

		int res = 0;

		return res = potereDellaNatura();
	}


}

L’elfo ha tutte le proprietà dell’umanoide. Elfo è classe figlio di Umanoide tanto quanto Umano e Nano. L’elfo ha come proprietà in più un nome, una specie specifica, un villaggio di appartenenza, capacità di arrampicamento e leggerezza.

Anche l’elfo ha un metodo per l’aumento della vita, un metodo per scegliere l’arma che aumenterà il suo attacco a seconda dell’arma scelta e ha dei metodi per ricalcolare tutte le proprietà dell’elfo come tale (attacco, difesa, agilità, velocità).

Il secondo umanoide è l’umano:

package entities;

import java.util.Random;

public class Umano extends Umanoide {

	private String nome;

	private String specie;

	private String citta;

	private int flagello;

	private boolean uomoOnore;

	private int tattica;

	public Umano(int vita, int attacco, int difesa, int velocita, int agilita, int scioltezza, boolean piedeAchille,
			int ingenieristica, String nome, String specie, String citta, int flagello, boolean uomoOnore,
			int tattica) {
		super(vita, attacco, difesa, velocita, agilita, scioltezza, piedeAchille, ingenieristica);
		this.nome = nome;
		this.specie = specie;
		this.citta = citta;
		this.flagello = flagello;
		this.uomoOnore = uomoOnore;
		this.tattica = tattica;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getSpecie() {
		return specie;
	}

	public void setSpecie(String specie) {
		this.specie = specie;
	}

	public String getCitta() {
		return citta;
	}

	public void setCitta(String citta) {
		this.citta = citta;
	}

	public int getFlagello() {
		return flagello;
	}

	public void setFlagello(int flagello) {
		this.flagello = flagello;
	}

	public boolean isUomoOnore() {
		return uomoOnore;
	}

	public void setUomoOnore(boolean uomoOnore) {
		this.uomoOnore = uomoOnore;
	}

	public int getTattica() {
		return tattica;
	}

	public void setTattica(int tattica) {
		this.tattica = tattica;
	}

	@Override
	public String toString() {
		return "Umano [nome=" + nome + ", specie=" + specie + ", citta=" + citta + ", flagello=" + flagello
				+ ", uomoOnore=" + uomoOnore + ", tattica=" + tattica + "]";
	}

	public int lealta() {

		int res = 0;
		
		if(uomoOnore) {
			
			res = 50;
		}

		return res;
	}

	public int Arma() {

		int res = 0;
		
		String arma = "";
		
		Random dice = new Random();
		
		int numero = dice.nextInt(3)+1;
		
		switch(numero) {
		
		case 1:
			
		arma = "spada";
		
		break;
		
		case 2: 
			
		arma = "doppia pistola";
		
		break;
		
		case 3:
			
		arma = "doppia alabarda";
		
		break;
		
		
		}
		
		
		switch(arma) {
		
		case "spada":
			
			res = 8;
			
			break;
			
		case "doppia pistola":
			
			res = 10;
			
			break;
			
		case "doppia alabarda":
			
			res = 15;
			
			break;
		
		
		}
		

		return res;
	}
	
	public int attaccoUmano() {

		int res = 0;

		return res = getAttacco() + soloAttacco() + getIngenieristica() + getFlagello()
				+ Arma();
	}

	public int difesaUmano() {

		int res = 0;

		return res = getDifesa() + tuttaDifesa() + getTattica();

	}

	public int velocitaUmano() {

		int res = 0;

		return res = getVelocita() + isVelocissimo();
	}

	public int agilitaUmano() {

		int res = 0;

		return res = getAgilita() + getScioltezza();
	}

	public int aumentovitaUmano() {
		int res = 0;

		return res = lealta();
	}

}

L’umano è una specie di umanoide che possiede tutte le proprietà del guerriero e dell’umanoide. Inoltre l’umano ha un nome, è una specie particolare di umano, abita in una certa città, la capacità di essere un flagello e di sfruttare la tattica.

L’umano ha un metodo che gli aumenta la vita, un metodo per la scelta dell’arma e dei metodi per ricalcolare velocità, attacco, difesa e agilità.

L’ultimo degli umanoidi è il nano:

package entities;

import java.util.Random;

public class Nano extends Umanoide{
	
	private String nome;
	
	private String specie;
	
	private String fortezza;
	
	private int armaiolo;
	
	private boolean cocciuto;
	
	private int schivatutto;

	public Nano(int vita, int attacco, int difesa, int velocita, int agilita, int scioltezza, boolean piedeAchille,
			int ingenieristica, String nome, String specie, String fortezza, int armaiolo, boolean cocciuto,
			int schivatutto) {
		super(vita, attacco, difesa, velocita, agilita, scioltezza, piedeAchille, ingenieristica);
		this.nome = nome;
		this.specie = specie;
		this.fortezza = fortezza;
		this.armaiolo = armaiolo;
		this.cocciuto = cocciuto;
		this.schivatutto = schivatutto;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getSpecie() {
		return specie;
	}

	public void setSpecie(String specie) {
		this.specie = specie;
	}

	public String getFortezza() {
		return fortezza;
	}

	public void setFortezza(String fortezza) {
		this.fortezza = fortezza;
	}

	public int getArmaiolo() {
		return armaiolo;
	}

	public void setArmaiolo(int armaiolo) {
		this.armaiolo = armaiolo;
	}

	public boolean isCocciuto() {
		return cocciuto;
	}

	public void setCocciuto(boolean cocciuto) {
		this.cocciuto = cocciuto;
	}

	public int getSchivatutto() {
		return schivatutto;
	}

	public void setSchivatutto(int schivatutto) {
		this.schivatutto = schivatutto;
	}

	@Override
	public String toString() {
		return "Nano [nome=" + nome + ", specie=" + specie + ", fortezza=" + fortezza + ", armaiolo=" + armaiolo
				+ ", cocciuto=" + cocciuto + ", schivatutto=" + schivatutto + "]";
	}
	
	
	public int animoForte() {
		
		int res = 0;
		
		if(cocciuto) {
			
			res = 50;
			
		}
		
		return res;
	}
	
	public int Arma() {

		int res = 0;
		
		String arma = "";
		
		Random dice = new Random();
		
		int numero = dice.nextInt(3)+1;
		
		switch(numero) {
		
		case 1:
			
		arma = "pistola";
		
		break;
		
		case 2: 
			
		arma = "fucile a canna lunga";
		
		break;
		
		case 3:
			
		arma = "cannone";
		
		break;
		
		
		}
		
		
		switch(arma) {
		
		case "pistola":
			
			res = 8;
			
			break;
			
		case "fucile a canna lunga":
			
			res = 10;
			
			break;
			
		case "cannone":
			
			res = 15;
			
			break;
		
		
		}
		

		return res;
	}
	
	public int attaccoNano() {

		int res = 0;

		return res = getAttacco() + soloAttacco() + getIngenieristica() + getArmaiolo()
				+ Arma();
	}

	public int difesaNano() {

		int res = 0;

		return res = getDifesa() + tuttaDifesa() + getSchivatutto();
	}

	public int velocitaNano() {

		int res = 0;

		return res = getVelocita() + isVelocissimo();
	}

	public int agilitaNano() {

		int res = 0;

		return res = getAgilita() + getScioltezza();
	}

	public int aumentovitaNano() {

		int res = 0;

		return res = animoForte();
	}

	
	
	
}

Il nano ha un nome, appartiene ad una specie, ha una fortezza di appartenenza, è abile nelle armi ed, essendo basso, schiva meglio i colpi.

Il nano ha anche un metodo per l’aumento della vita, uno per la scelta dell’arma e dei metodi che calcolino attacco, velocità, difesa, agilità reali.

Ogni essere ha dunque proprietà sue, proprietà della sua classe padre (Mostro o Umanoide) e proprietà generali del guerriero. In ogni creatura troviamo dei metodi che ricalcolano l’attacco, la difesa, la velocità e l’agilità.

Queste in origine erano delle proprietà del guerriero. Tuttavia, con l’aumento delle proprietà derivate dalle classi figlio, essendo queste proprietà delle variabili che aumentano difesa, attacco, agilità o velocità a seconda dei casi, ne consegue la necessità di ricalcolare attacco, difesa, agilità e velocità, tenendo conto di tutti questi aumenti.

Oltre a questo, ho pensato che fosse opportuno avere quattro metodi generali a parte che calcolassero attacco, velocità, agilità e difesa a seconda che si tratta di mostro o umanoide e a seconda del tipo di mostro e di umanoide.

Prima di creare una classe che facesse questo, ho pensato di creare un’interfaccia.

L’interfaccia è una classe che contiene solo firme di metodi. In una interfaccia i metodi sono solo dichiarati, ma non solo implementati.

Questa è la mia interfaccia:

package calcolo;

import entities.Mostro;
import entities.Umanoide;

public interface ICalcoloPotenze {
	
	public int attaccoM(Mostro mostro);
	public int difesaM(Mostro mostro);
	public int velocitaM(Mostro mostro);
	public int agilitaM(Mostro mostro);
	public int vitaM(Mostro mostro);
	public int auVitaM(Mostro mostro);
	
	public int attaccoU(Umanoide umanoide);
	public int difesaU(Umanoide umanoide);
	public int velocitaU(Umanoide umanoide);
	public int agilitaU(Umanoide umanoide);
	public int vitaU(Umanoide umanoide);
	public int auVitaU(Umanoide umanoide);
}

Per questa classe ho creato un altro package dove inserire questa interfaccia e la classe che implementa questa interfaccia.

In questa interfaccia ho definito i metodi di attacco, difesa, agilità, velocità, aumento vita, a seconda che si trattasse di un umanoide o di un mostro. A seconda dei due casi, ho inserito come parametro del metodo un oggetto di tipo Mostro e un oggetto di tipo Umanoide.

Dopo l’interfaccia ho creato questa classe che implementa l’interfaccia:

package calcolo;

import entities.Elfo;
import entities.Mostro;
import entities.Nano;
import entities.Orco;
import entities.Troll;
import entities.Umano;
import entities.Umanoide;
import entities.Vampiro;

public class CalcoloPotenze implements ICalcoloPotenze {

	@Override
	public int attaccoM(Mostro mostro) {
		int res = 0;

		if (mostro instanceof Troll) {

			res = ((Troll) mostro).attaccoTroll();
		}

		else if (mostro instanceof Orco) {

			res = ((Orco) mostro).attaccoOrco();
		}

		else if (mostro instanceof Vampiro) {

			res = ((Vampiro) mostro).attaccoVampiro();
		}

		return res;
	}

	@Override
	public int difesaM(Mostro mostro) {

		int res = 0;

		if (mostro instanceof Troll) {

			res = ((Troll) mostro).difesaTroll();
		}

		else if (mostro instanceof Orco) {

			res = ((Orco) mostro).difesaOrco();
		}

		else if (mostro instanceof Vampiro) {

			res = ((Vampiro) mostro).difesaVampiro();
		}

		return res;

	}

	@Override
	public int velocitaM(Mostro mostro) {

		int res = 0;

		if (mostro instanceof Troll) {

			res = ((Troll) mostro).velocitaTroll();
		}

		else if (mostro instanceof Orco) {

			res = ((Orco) mostro).velocitaOrco();
		}

		else if (mostro instanceof Vampiro) {

			res = ((Vampiro) mostro).velocitaVampiro();
		}

		return res;

	}

	@Override
	public int agilitaM(Mostro mostro) {

		int res = 0;

		if (mostro instanceof Troll) {

			res = ((Troll) mostro).agilitaTroll();
		}

		else if (mostro instanceof Orco) {

			res = ((Orco) mostro).agilitaOrco();
		}

		else if (mostro instanceof Vampiro) {

			res = ((Vampiro) mostro).agilitaVampiro();
		}

		return res;

	}

	@Override
	public int attaccoU(Umanoide umanoide) {

		int res = 0;

		if (umanoide instanceof Umano) {

			res = ((Umano) umanoide).attaccoUmano();
		}

		else if (umanoide instanceof Elfo) {

			res = ((Elfo) umanoide).attaccoElfo();
		}

		else if (umanoide instanceof Nano) {

			res = ((Nano) umanoide).attaccoNano();
		}

		return res;
	}

	@Override
	public int difesaU(Umanoide umanoide) {

		int res = 0;

		if (umanoide instanceof Umano) {

			res = ((Umano) umanoide).difesaUmano();
		}

		else if (umanoide instanceof Elfo) {

			res = ((Elfo) umanoide).difesaElfo();
		}

		else if (umanoide instanceof Nano) {

			res = ((Nano) umanoide).difesaNano();
		}

		return res;

	}

	@Override
	public int velocitaU(Umanoide umanoide) {

		int res = 0;

		if (umanoide instanceof Umano) {

			res = ((Umano) umanoide).velocitaUmano();
		}

		else if (umanoide instanceof Elfo) {

			res = ((Elfo) umanoide).velocitaElfo();
		}

		else if (umanoide instanceof Nano) {

			res = ((Nano) umanoide).velocitaNano();
		}

		return res;

	}

	@Override
	public int agilitaU(Umanoide umanoide) {

		int res = 0;

		if (umanoide instanceof Umano) {

			res = ((Umano) umanoide).agilitaUmano();
		}

		else if (umanoide instanceof Elfo) {

			res = ((Elfo) umanoide).agilitaElfo();
		}

		else if (umanoide instanceof Nano) {

			res = ((Nano) umanoide).agilitaNano();
		}

		return res;

	}

	@Override
	public int vitaM(Mostro mostro) {

		int res = 0;

		if (mostro instanceof Troll) {

			res = ((Troll) mostro).getVita();
		}

		else if (mostro instanceof Orco) {

			res = ((Orco) mostro).getVita();
		}

		else if (mostro instanceof Vampiro) {

			res = ((Vampiro) mostro).getVita();
		}

		return res;

	}

	@Override
	public int vitaU(Umanoide umanoide) {

		int res = 0;

		if (umanoide instanceof Umano) {

			res = ((Umano) umanoide).getVita();
		}

		else if (umanoide instanceof Elfo) {

			res = ((Elfo) umanoide).getVita();
		}

		else if (umanoide instanceof Nano) {

			res = ((Nano) umanoide).getVita();
		}

		return res;

	}

	@Override
	public int auVitaM(Mostro mostro) {

		int res = 0;

		if (mostro instanceof Troll) {

			res = ((Troll) mostro).aumentovitaTroll();
		}

		else if (mostro instanceof Orco) {

			res = ((Orco) mostro).aumentovitaOrco();
		}

		else if (mostro instanceof Vampiro) {

			res = ((Vampiro) mostro).aumentovitaVampiro();
		}

		return res;

	}

	@Override
	public int auVitaU(Umanoide umanoide) {

		int res = 0;

		if (umanoide instanceof Umano) {

			res = ((Umano) umanoide).aumentovitaUmano();
		}

		else if (umanoide instanceof Elfo) {

			res = ((Elfo) umanoide).aumentovitaElfo();
		}

		else if (umanoide instanceof Nano) {

			res = ((Nano) umanoide).aumentovitaNano();
		}

		return res;

	}

}

Qui vediamo il secondo dei principi della programmazione orientata agli oggetti: il polimorfismo.

Il polimorfismo lo vediamo in atto in diversi casi. Qui lo vediamo quando usiamo l’annotazione override. L’override serve per sovrascrivere un determinato metodo. In questo caso sovrascriviamo il metodo implementato dall'interfaccia.

Ora i metodi dell’interfaccia avranno un corpo.

Per ogni metodo dobbiamo fare dei calcoli. Prendiamo, ad esempio, il metodo del calcolo dell’attacco di un mostro. A seconda del mostro che arriva come parametro, il metodo intende calcolare l’attacco complessivo del mostro, vedendo prima se si tratta di un orco, un troll o un vampiro.

Per fare questo usiamo un if e dentro l’if usiamo la funzione “instanceof” che va a vedere se quell'oggetto è un’istanza di una determinata classe. Dopo di ché, se è istanza di Troll, ad esempio, assegno alla risposta la somma dell’attacco e gli aumenti dell’attacco specifici del troll. Faccio la stessa cosa per ogni specie e oltre che per l’attacco, anche per la difesa, per l’agilità e per la velocità.

La classe eseguibile per il gioco vero e proprio l’ho creata in un package a parte che ho chiamato “game”.

Questa è classe eseguibile:

package game;

import java.util.Random;
import java.util.Scanner;

import calcolo.CalcoloPotenze;
import entities.Elfo;
import entities.Mostro;
import entities.Nano;
import entities.Orco;
import entities.Troll;
import entities.Umano;
import entities.Umanoide;
import entities.Vampiro;

public class GameFantasy {

	public static void main(String[] args) {

		Mostro vampiro = new Vampiro(800, 20, 20, 15, 15, true, 10, 10, "Morgan", "Vampiro oscuro", "Drakkar", true, 5,
				10);

		Umanoide umano = new Umano(800, 15, 20, 15, 20, 10, true, 15, "Nik", "nordico", "Solvagar", 10, true, 10);

		Mostro troll = new Troll(800, 30, 10, 5, 5, true, 20, 15, "Olum", "troll di roccia", "guardiani della montagna",
				10, true, 20);

		Mostro orco = new Orco(800, 25, 15, 10, 10, true, 15, 15, "Orgur", "orchi neri", "Teschio di fuoco", 10, true,
				10);

		Umanoide nano = new Nano(800, 20, 25, 10, 5, 5, true, 30, "Mirmir", "nani delle nevi", "Mondar", 20, true, 5);

		Umanoide elfo = new Elfo(800, 8, 20, 20, 30, 30, true, 3, "Jessica", "elfa del bosco", "Elmenor", 10, true, 15);

		CalcoloPotenze calc = new CalcoloPotenze();

		int vitaGiocatore = 800;

		int vitaNemico = 800;

		Scanner tastiera = new Scanner(System.in);

		Random dice = new Random();

		System.out.println("Quale razza scegli?");

		String razza = tastiera.nextLine();

		if (razza.equalsIgnoreCase("mostro")) {

			System.out.println("Quale specie scegli");

			String specie = tastiera.nextLine();

			Mostro giocatore = null;

			switch (specie) {

			case "troll":

				giocatore = troll;

				break;

			case "orco":

				giocatore = orco;

				break;

			case "vampiro":

				giocatore = vampiro;

				break;
			}

			Umanoide nemico = null;

			int randomS = dice.nextInt(3) + 1;

			switch (randomS) {

			case 1:

				nemico = umano;

				break;

			case 2:

				nemico = elfo;

				break;

			case 3:

				nemico = nano;

				break;

			}

			while (vitaGiocatore > 0 && vitaNemico > 0) {

				int numeromagicoG = dice.nextInt(40);

				int numeromagicoN = dice.nextInt(40);

				if ((calc.velocitaM(giocatore) + numeromagicoG) > (calc.velocitaU(nemico) + numeromagicoN)) {
					
					if((calc.attaccoM(giocatore) + calc.agilitaM(giocatore) + numeromagicoG) > (calc.difesaU(nemico) + calc.agilitaU(nemico) + numeromagicoN) ) {

					vitaNemico = vitaNemico - ((calc.attaccoM(giocatore) + calc.agilitaM(giocatore) + numeromagicoG)
				
							- (calc.difesaU(nemico) + calc.agilitaU(nemico) + numeromagicoN));

					}
					
				} else {
					
					if((calc.attaccoU(nemico) + calc.agilitaU(nemico) + numeromagicoN) > (calc.difesaM(giocatore) + calc.agilitaM(giocatore) + numeromagicoG)) {

					vitaGiocatore = vitaGiocatore - ((calc.attaccoU(nemico) + calc.agilitaU(nemico) + numeromagicoN)
							- (calc.difesaM(giocatore) + calc.agilitaM(giocatore) + numeromagicoG));

					}
					
				}

				System.out.println("Vita del Giocatore: " + vitaGiocatore + "Vita del Nemico: " + vitaNemico);

			}

		}

		else if (razza.equalsIgnoreCase("umanoide")) {

			System.out.println("Quale specie scegli");

			String specie = tastiera.nextLine();

			Umanoide giocatore = null;

			switch (specie) {

			case "umano":

				giocatore = umano;

				break;

			case "elfo":

				giocatore = elfo;

				break;

			case "nano":

				giocatore = nano;

			}

			Mostro nemico = null;

			int randomS = dice.nextInt(3) + 1;

			switch (randomS) {

			case 1:

				nemico = troll;

				break;

			case 2:

				nemico = orco;

				break;

			case 3:

				nemico = vampiro;

				break;
			}

			while (vitaGiocatore > 0 && vitaNemico > 0) {

				int numeromagicoG = dice.nextInt(40);

				int numeromagicoN = dice.nextInt(40);

				if (calc.velocitaU(giocatore) + numeromagicoG > calc.velocitaM(nemico) + numeromagicoN) {

					if ((calc.attaccoU(giocatore) + calc.agilitaU(giocatore)
							+ numeromagicoG) > (calc.difesaM(nemico) + calc.agilitaM(nemico) + numeromagicoN)) {

						vitaNemico = vitaNemico - ((calc.attaccoU(giocatore) + calc.agilitaU(giocatore) + numeromagicoG)
								- (calc.difesaM(nemico) + calc.agilitaM(nemico) + numeromagicoN));

					}

				} else {

					if ((calc.attaccoM(nemico) + calc.agilitaM(nemico)
							+ numeromagicoN) > (calc.difesaU(giocatore) + calc.agilitaU(giocatore) + numeromagicoG)) {

						vitaGiocatore = vitaGiocatore - ((calc.attaccoM(nemico) + calc.agilitaM(nemico) + numeromagicoN)
								- (calc.difesaU(giocatore) + calc.agilitaU(giocatore) + numeromagicoG));

					}
				}

				System.out.println("Vita del Giocatore: " + vitaGiocatore + "Vita del Nemico: " + vitaNemico);

			}

		}

	}

}

Questo pezzo di codice merita un’analisi molto più dettagliata.

In alto, all'interno del metodo main, ho creato i miei oggetti, ognuno per personaggio. Ad ogni oggetto ho assegnato dei valori specifici che corrispondono ai valori delle proprietà che l’oggetto possiede.

Ogni valori rispecchia delle differenze: ad esempio l’elfo è più agile del troll, ma il troll è più forte dell’elfo.

Solamente la vita è uguale per tutti. Avrete notato che la classe formale degli oggetti è diversa dalla classe concreta. Ad esempio l’elfo ha come classe formale “Umanoide”, mentre come classe concreta “Elfo”.

Questo fatto segue il principio del polimorfismo, che consiste, appunto, nella capacità del codice di assumere più forme.

Ho creato anche l’oggetto per classe dove eseguono i calcoli di attacco, difesa, agilità e velocità. Ho poi inserito due variabili, una per vita del giocatore e l’altra per la vita del nemico.

In pratica l’intero gioco è uno scontro a due: noi siamo il giocatore e ci scontriamo contro un nemico.

Ho fatto in modo che il giocatore se è mostro o umanoide, allora non avrà un nemico dello stesso tipo, altrimenti sarebbe potuto succedere che, ad esempio, l’orco dovesse scontrarsi contro se stesso. Così prima creo uno Scanner, per poi chiedere all'utente se preferisce che il giocatore sia mostro o umanoide, poi l’utente deve scegliere la specie di mostro o di umanoide a seconda, quindi se è orco, elfo, umano, ecc.

Se il giocatore è Mostro, allora il nemico sarà Umanoide e viceversa. Il tipo di nemico viene scelto a caso dal computer tra le tre opzioni. Fintanto che la vita del nemico o quella del giocatore sono maggiori di 0, il combattimento continua. Per fare questo ho creato un while che avesse proprio questa come condizione. Poi ho creato due numeri magici, uno per il giocatore e l’altro per il nemico, perché fossero aggiunti alla velocità, alla difesa, all’agilità e all'attacco.

Questi numeri magici sono numeri random che rendono variabili attacco, difesa, velocità e agilità. Altrimenti se un nemico è più veloce del giocatore, attacca sempre e solo lui. Dopo verifico se la velocità del giocatore è maggiore di quella del nemico oppure il contrario.

A seconda di chi risulta più veloce, quella persona sarà l’attaccante e l’altro il difensore.

Eseguo questo controllo con un if.

All'interno del corpo dell’if vedo i danni dell’attacco sulla vita, sottraendo alla vita del difensore la differenza tra l’attacco dell’attaccante, a cui si somma l’agilità e il numero magico, e la difesa del difensore, a cui si somma l’agilità e il numero magico.

Prima di fare questo, però, controllo che questa differenza non dia un numero negativo, altrimenti il rischio è che la vita non diminuisca mai, ma, anzi, aumenti, generando così un possibile loop infinito derivato da una battaglia infinita dove le vite non vanno mai a zero, ma aumentano pure. Solamente quando almeno la vita del giocatore o quella del nemico arrivano a 0, il gioco finisce.

Quando eseguiamo il codice, dopo aver fatto le nostre scelte, il combattimento ci apparirà come un insieme di attimi dei quali ci vengono restituite le due vite avanzate dopo gli attacchi e le difese. Questo è solo un abbozzo di un gioco.

Per fare un gioco vero bisognerebbe metterci la grafica, avere dei personaggi che si muovono e sopratutto bisognerebbe lavorarci ancora molto di più.

Infatti, per ora, gli umanoidi sono molto in vantaggio sui mostri perché sono molto veloci e attaccano quasi sempre loro. Ma fatte le correzioni dovute e lavorando parecchio sul gioco, soprattutto sulla grafica, questo potrebbe evolversi in un progetto completo.

8) Cosa creare con Java

Java è un linguaggio che ci permette di fare moltissime cose. Prima di tutto, come abbiamo visto, possiamo creare dei giochi. Possiamo anche creare software, siti web o applicazioni web e app android.

a) Java Gui

Per lavorare sul Gui in Java bisogna importare delle librerie aggiuntive. Normalmente si lavora principalmente su due tipi di librerie Awt e Swing.

  • Awt sta per Abstract window toolkit ed è orientato allo sviluppo di interfacce grafiche.
  • Swing è una estensione di Awt.

Di solito si lavora in questo modo: creiamo un frame come pannello e su questo pannello aggiungiamo tutti i nostri oggetti. Creiamo il JFrame da swing e poi creiamo quegli oggetti che ci servono: JButton, JTextarea, JCheckbox, ecc.

Possiamo dunque creare il nostro menu come in tutti i programmi, creare dei bottoni, assegnargli delle funzionalità e così via. Ad ogni elemento, tramite awt, possiamo assegnare degli eventi.

Per esempio, se l’utente preme un bottone o seleziona un elemento dal menu, possiamo far accadere qualcosa in risposta a quell'evento.

Con swing e awt abbiamo gli strumenti base per poter costruire, ad esempio, un programma di grafica o uno di disegno. Molto interessante per sviluppare la grafica è anche JavaFx, una libreria usata per sviluppare applicazioni Dekstop.

b) Java EE:

In Java si distingue tra Java SE (Java Standard Edition) e Java EE (Java Enterprise Edition).

Java EE o j2ee viene usato normalmente per lo sviluppo delle applicazioni web. Si passa dunque dal classico progetto Java ai progetti web dinamici o anche a Maven.

Per usare Java nel contesto del web normalmente si usa un servizio come Tomcat.

Tomcat è un web server che ci permette di eseguire applicazioni web. I progetti web sono molto diversi dai progetti normali. Anche questi hanno una cartella sorgente dove solitamente stanno tutti i file Java, ma hanno anche una cartella Web-Content dove si salvano i file html, Jsp (JavaSever Page) e xml che usiamo per la nostra applicazione web.

Con j2ee possiamo sviluppare siti web, blog, e-commerce, social network, ecc.

c) Java Android
Il sistema operativo Android è scritto in Java. Grazie al programma Android Studio possiamo programmare app Android. Android Studio è un IDE per lo sviluppo esattamente come Eclipse.

Da quel che so, Java può essere usato anche per sviluppare intelligenza artificiale, ma le sue capacità sono limitate. Alla fine di questo lungo articolo spero di aver mostrato un legame tra la filosofia e la programmazione Java.

Spero anche di essere riuscito a mettere in evidenza quanto potente può essere questo linguaggio e perché varrebbe seriamente la pena di studiarlo anche negli ambienti della filosofia.