edutecnica
 


Polimorfismo  
    

Col termine polimorfismo si intende la possibilità di assegnare lo stesso nome a procedure che fanno cose diverse;

class polimorfismo{
public static void main (String args []){
System.out.println(somma("due","tre"));
System.out.println(somma(2,3));
}//fine main
static int somma(int a,int b){ return a+b;}
static String somma(String a,String b){ return a+b;}
}// fine classe

l'output sarebbe:
duetre
5

Esistono due metodi che hanno lo stesso identificatore ( nome ) ma differiscono per il tipo dei parametri passati.
Il corpo del metodo è lo stesso ma uno esegue una somma fra numeri mentre l'altro esegue una concatenazione fra stringhe.
Si parla di somma() come di un metodo sovraccarico ( overload ) e si tratta certamente di un caso di polimorfismo dato che la funzione somma() può assumere un comportamento o un altro a secondo dei parametri passati. Un altro esempio è il seguente;

class overload {
public static void main(String[] args) {
D dx=new D();
D dy=new D(5);
System.out.println(dx.get());
System.out.println(dy.get());
} //fine main
}//fine classe
class D {
private int p;
D(){p=lancia();} //I° costruttore
D(int j){p=j*lancia();} //II° costruttore
private int lancia(){ return (1+(int)(Math.random()* 6));}
int get(){return p;}
}//fine classe D

l'output potrebbe essere:
4
30

Si tratta di un programma che simula il risultato del lancio di un dado nel caso venga invocato il costruttore D(). Ma quando viene chiamato D(5)viene ottenuto il risultato del lancio di un dado moltiplicato per 5.
L'overload dei metodi consiste nel creare più metodi con lo stesso nome ma con segnatura diversa
( numero e ordine dei parametri passati ).
Sarà eventualmente il compilatore a decidere in fase di run-time (late-binding) quale metodo applicare a secondo del tipo di parametri ricevuti in input.

Il seguente programma simula invece il lancio di un dado e di due dadi in successione.

class override {
public static void main(String[] args) {
D d=new D();
DD dd=new DD();
System.out.println(d.get());
System.out.println(dd.get());
} //fine main
}//fine classe
class D {
protected int p;
D(){p=lancia();}
int lancia(){ return (1+(int)(Math.random()* 6));}
int get(){return p;}
}//fine classe D
class DD extends D{
DD(){p=lancia();}
int lancia(){ //usa il metodo della classe D e lo riscrivo
   int j;
   j=super.lancia();
   j+=super.lancia();
   return j;
}//fine lancia
}//fine classe DD

un risultato possibile sarebbe:
2
9

La classe D simula il lancio di un dado attraverso il metodo lancio().
La classe DD eredita dalla classe D tutto: l'attributo p, il metodo get() e anche il metodo lancia() e lo sovrascrive , cioè lo riutilizza e simultaneamente lo ridefinisce: questo è un esempio di override di un metodo.


Classi Astratte ed interfacce  
    

L'ereditarietà è una peculiarità tipica della programmazione ad oggetti; essa permette di definire nuove classi ereditando metodi ed attributi delle classi genitrici (ancestor).
In pratica è un meccanismo che permette di specializzare ulteriormente classi già esistenti. .
Ipotizziamo un programma che debba manipolare figure geometriche:

class geo {
public static void main (String args []) {
     Cerchio c = new Cerchio(2);
     Quadrato q = new Quadrato(2);
     System.out.println(c.area());
     System.out.println(q.area());
}//fine main
}// fine classe geo

class Cerchio {
private int ray;
Cerchio(int r) {ray=r;}//costruttore
public double area(){
      return ray*ray*Math.PI;}
}//fine classe Cerchio

class Quadrato {
private int lato;
Quadrato(int l) {lato=l;}//costruttore
public double area(){return lato*lato;}
}//fine classe Quadrato


Se volessimo 'scaricare' il main() dall'onere di contenere l'operazione di stampa possiamo pensare di derivare le due classi Quadrato e Cerchio da una classe più generica:Figura , delegando a quest'ultima la responsabilità di stampare l'area delle singole figure. Infatti, il meccanismo opposto all'idea di specializzazione è il concetto di generalizzazione.

class geo {
public static void main (String args []) {
     Cerchio c = new Cerchio(2);
     Quadrato q = new Quadrato(2);
     c.stampaArea();
     q.stampaArea();
}//fine main
}// fine classe geo

class Figura {
public void stampaArea() {
     System.out.println(area()); }
}//fine classe Figura

class Cerchio extends Figura{
private int ray;
Cerchio(int r) {ray=r;}//costruttore
public double area(){
      return ray*ray*Math.PI;}
}//fine classe Cerchio

class Quadrato extends Figura{
private int lato; Quadrato(int l) {lato=l;}//costruttore
public double area(){return lato*lato;}
}//fine classe Quadrato


Non funziona!
In quanto il compilatore non riesce a compilare il metodo stampaArea() che usa un metodo area() che non è visibile dalla classe Figura: si ottiene un errore in compilazione di questo tipo:

geo.java:38: cannot find symbol
symbol : method area()
location: class Figura
System.out.println(area());


Si potrebbe definire stampaArea() in ogni sottoclasse, ma questo sarebbe uno spreco, una duplicazione di codice, non è ovviamente possibile definire area() in Figura, dato che ogni figura ha una sua formula per il calcolo dell'area.
La soluzione corretta è definire astratta la classe Figura e inserirvi un metodo, anch'esso astratto, per l'area:

Una classe astratta è una classe non istanziabile, anche se può essere dotata di costruttori essa è dotata di metodi astratti che sono implicitamente pubblici di cui non è definito il corpo, la definizione del (loro) corpo è delegato alle sottoclassi della classe astratta.

class geo {
public static void main (String args []) {
Cerchio c = new Cerchio(2);
Quadrato q = new Quadrato(2);
c.stampaArea(); q.stampaArea();
}//fine main
}// fine classe geo

abstract class Figura {
abstract double area();
public void stampaArea(){System.out.println(area());}//fine metodo stampaArea }//fine classe Figura

class Cerchio extends Figura{
private int ray;
Cerchio(int r) {ray=r;}//costruttore
public double area(){return ray*ray*Math.PI;}
}//fine classe Cerchio

class Quadrato extends Figura{
private int lato; Quadrato(int l) {lato=l;}//costruttore
public double area(){return lato*lato;}
}//fine classe Quadrato


d'altra parte, tutte le figure geometriche hanno un'area (e quindi la classe Figura ha un metodo area(),
ma non è possibile dire come calcolare l'area per una generica figura (e quindi il metodo area() in Figura è astratto mentre viene implementato nelle sottoclassi).
Inoltre, non sarà possibile istanziare la classe Figura, ma questo è giustificato dal fatto che il programma deve creare specifiche figure.

Le classi astratte servono a definire il comportamento comune a tutte le classi della gerarchia e sono usate per sfruttare il polimorfismo e il late binding, dato che la sopraclasse può essere non completamente specificata. .
Una classe A viene definita astratta quando esiste una funzione che ha senso associare ad essa,
ma il cui codice non può essere specificato a livello di A, mentre può essere specificato a livello delle sottoclassi di A.

Si dice, in tal caso, che tale funzione è astratta in A.

Si nota come la classe astratta può avere funzioni e campi dati non astratti;
essa è astratta perché contiene almeno un metodo astratto.

Una classe astratta :
• Ha il modificatore abstract nell'intestazione della classe.
• È una classe incompleta per definizione, perchè contiene metodi astratti.
• È un segnaposto nella gerarchia delle classi e modella un concetto generico.
• La classe abstract non può essere instanziata ma deve essere specializzata (ulteriormente).
• Quando una classe è figlia di una classe astratta deve implementare i metodi. astratti della classe padre , in caso contrario rimane anch'essa astratta.

Una classe deve essere dichiarata astratta se:
• Contiene la dichiarazione di un metodo astratto
• Eredita un metodo astratto dalla superclasse (classe o interfaccia) e non ne fornisce l'implementazione

Un metodo astratto non può essere definito final, static o private
• Perché dovrà essere sovrascritto (final)
• Perchè non ha ancora una definizione (static)
• Perchè non potrebbe essere ereditato (private)

Se A è una classe astratta, allora:
Non possiamo creare direttamente oggetti che sono istanze di A.
Non esistono istanze dirette di A: gli oggetti che sono istanze di A lo sono indirettamente e implicitamente.

Per una classe astratta possiamo:
• Definire variabili o campi di altri classi (ovvero, riferimenti) di tipo A (durante l'esecuzione,conterranno indirizzi di oggetti di classi non astratte che sono sottoclassi di A).
- Usare normalmente i riferimenti (tranne che per creare nuovi oggetti), ad esempio: definire funzioni che prendono come argomento un riferimento di tipo A, restituire riferimenti di tipo A, ecc.

E' noto che in Java non è possibile estendere più di una classe, come in C++.
Non è dunque consentita la scrittura:

class C extends A, B {
. . .
}

java classe astratta esempio

Questa peculiarità viene definita ereditarietà multipla. Si crea, infatti una ambiguità se nelle classi genitrici viene contenuto uno stesso metodo m().

Infatti, una volta istanziato l'oggetto c della classe C, quale metodo m() deve essere applicato? Quello della classe A o quello della classe B?

Comunque, per soddisfare alle esigenze legate alla comodità dell'ereditarietà multipla viene messo a disposizione uno strumento potente: le interfacce.
Le differenze fra una interfaccia e una classe sono:

I) Viene usata la parola interface invece che class.
Adifferenza delle classi astratte una interfaccia non può avere costruttori.

II) Non viene fornita l'implementazione di nessun metodo di un'interfaccia ed implicitamente tutti i metodi di una interfaccia sono pubblici ed astratti anche se il programmatore non lo dichiara esplicitamente. Le notazioni public ed abstract,possono dunque, essere omesse.
Una interfaccia, è dunque, un contenitore di metodi astratti, non contiene variabili istanza.

III) Una classe può implementare un'interfaccia: per farlo deve fornire un'implementazione di tutti i metodi dell'interfaccia e usare la parola riservata implements nell'intestazione, subito dopo extends, altrimenti diventa astratta.

Apparentemente, una interfaccia sembra semplicemente una classe costituita da metodi astratti;
ma le altre differenze fra classe ed interfaccia sono:

IV) Le interface non si possono istanziare, ma una classe può implementare una (o più) interfacce.

V) Una classe può ereditare (extends) da un'unica sopraclasse diretta, ma può implementare (implements) più interfacce.

Qui di seguito un esempio di realizzazione del programma precedente con l'utilizzo di una interfaccia Scalatura con il mantenimento della classe astratta Figura.

interfaccia java esempio

class geo {
public static void main (String args []) {
Quadrato q = new Quadrato(2);
q.stampaArea();
q.redux(50);
q.stampaArea();
}//fine main
}// fine classe geo

interface Scalatura {
public void redux (int scala); //se scala=100 la figura rimane invariata
}//fine interfaccia Scalatura

abstract class Figura {
abstract double area();
public void stampaArea() { System.out.println(area()); }
}//fine classe Figura

class Cerchio extends Figura implements Scalatura {
private int ray;
Cerchio(int r) {ray=r;}//costruttore
public double area(){return ray*ray*Math.PI;}
public void redux (int scala) {ray=ray*scala/100;}
}//fine classe Cerchio

class Quadrato extends Figura implements Scalatura {
private int lato; Quadrato(int l) {lato=l;}//costruttore
public double area(){return lato*lato;}
public void redux (int scala) {lato=lato*scala/100;}
}//fine classe Quadrato


Qui si vede come l'interfaccia, obbliga (se no dà un errore di compilazione) le classi che implementano l'interfaccia a contenere il metodo redux che può avere anche un corpo diverso nelle due classi implementate.
Quindi, possiamo dire che in Java non è supportata l'ereditarietà multipla, ma una classe può però implementare più interfacce definendo per essa diversi comportamenti.



edutecnica