Reificazione, si è una parola Italiana …
Processo mentale mediante il quale a concetti astratti viene assegnata consistenza di cose concrete
Nella flosofa di Marx, processo secondo cui nell'economia capitalistica l'uomo e il suo lavoro sono ridotti al valore della merce che producono
La reifcazione è una fallacia o un'ambiguità quando un'astrazione (una credenza astratta o un costrutto ipotetico) viene trattata come se fosse un concreto evento reale o un'entità fsica
Nell'ambito dell'Ingegneria della conoscenza, e in particolare della defnizione di ontologie, la reifcazione è una rappresentazione indiretta che prevede l'utilizzo di determinate espressioni del linguaggio per descrivere entità del mondo reale intuitivamente associate a espressioni di tipo diverso. L'esempio tipico di reifcazione è costituito dall'utilizzo di individui per rappresentare classi.
Nel contesto della programmazione orientata agli oggetti si defnisce reifcazione il procedimento di creazione di un modello di dati basato su un concetto astratto predefnito. Mediante la reifcazione, un computer può compiere elaborazioni riguardanti un'entità astratta come se si trattasse di un insieme qualsiasi di dati di altro tipo
Non sempre il compilatore è in grado di capire se un cast verso un tipo non reifcabile avrà successo I sistemi di tipi (dei linguaggi di programmazione orientati agli oggetti) non sono perfetti e non possono individuare situazioni di successo come può fare un (buon) programmatore Per questo motivo un cast verso un tipo non reifcabile non genera un errore ma un warning
In java un tipo è reifcabile se il tipo è completamente identifcabile a runtime, ovvero se la type erasure non rimuove informazioni utili
In java il tipo di un array viene reifcato con il tipo dei suoi componenti Un tipo parametrico non viene reifcato con il suo parametro di tipo List<?> è equivalente a List<? extends Object> (nel senso di passaggio di parametri) ma il primo è reifcabile il secondo no
I test instanceof ed i cast dipendono dall'esame a runtime dei tipi degli oggetti coinvolti e quindi dalla reifcazione Test di istanza verso un tipo non reifcabile → errore (di compilazione) Cast ad un tipo non reifcabile → genera (di norma) un warning
public class Myinteger { private final int value; public boolean equals(Object o) { Myinteger è reificabile → il cast non genere warning if (o instanceof Myinteger){ // ynteger è reificabile → compila return this.value == ((Myinteger)o).value; } else return false; // Myinteger è reificabile → il cast non genere warning } ... } </code java> ===== Equals su liste ===== <code> public abstract class MiaLista<E> extends AbstractCollection<E> implements List<E>{ public boolean equals(Object o){ if ( o instanceof List<E>){ Iterator<E> it1 = iterator(); Iterator<E> it2 = ((List<E>)o ).iterator(); while (it1.hasNext() && it2.hasNext()){ E e1 = it1.next(); E e2 = it2.next(); if (! ( e1 == null ? e2== null: e1.equals(e2) ) ){ return false; } } } } return !it1.hasNext() && !it2.hasNext(); } else return false;
public abstract class MiaLista<E> extends AbstractCollection<E> implements List<E>{ public boolean equals(Object o){ if ( o instanceof List<E>){ Iterator<E> it1 = iterator(); // Errore di compilazione! List<E> non è reificabile Iterator<E> it2 = ((List<E>)o ).iterator(); while (it1.hasNext() // Unchecked cast warning! && it2.hasNext()){ ...
public abstract class MiaListaOK<E> extends AbstractCollection<E> implements List<E>{ Iterator<E> it1 = iterator(); Iterator<?> it2 = ((List<?>)o ).iterator(); while (it1.hasNext() && it2.hasNext()){ return false; } } } return !it1.hasNext() && !it2.hasNext(); } else return false;
public abstract class MiaListaOK<E> extends AbstractCollection<E> implements List<E>{ public boolean equals(Object o){ if ( o instanceof List<?>){ Iterator<E> it1 = iterator(); // OK List<?> è reificabile Iterator<?> it2 = ( (List<?>)o ).iterator(); // List<?> è reificbaile e non vengono generati warning while (.... Object e2 = it2.next(); // Da una List<?> posso prelevare Object senza problemi
Test di istanza verso tipi non reifcabili generano sempre errore In alcune circostanze un cast ad un tipo non reifcabile può non generare warning
public static <T> List<T> asList(Collection<T> c ) throws IllegalArgumentException { if ( c instanceof List<?>){ // List<?> è reificabile → compila return (List<T>)c; } else throw new // Il cast non genera waring in quanto la sorgente del cast ha tipo Collection<T> // e ogni oggetto di questo tipo che implementa List deve avere come tipo List<T> IllegalArgumentException("Il parametro passato non e' un sottotipo di List<T>");
public static List<String> converti(List<?> oggetti){ for (Object obj: oggetti){ //Il cast “a logica” non fallirà mai if (!( obj instanceof String)){ throw new IllegalArgumentException( ... // OK String è reificabile }} return (List<String>)(List<?>)oggetti; } // Unchecked Cast ← il compilatore non è in grado di capire se il cast avrà successo o meno
Nota: è illegare “castare” una lista di oggetti ad una lista di stringhe, quindi serve il doppio cast
Gli array reifcano i loro componenti ovvero conservano a runtime i tipi dei loro componenti
Integer[] interi = new Integer[] {1,2,3}; // OK, gli array sono covarianti Number[] numeri = interi; numeri[2] = 3.14; // ArrayStoreException Double non è compatibile con il tipo reificato dell'array
public static <T> T[] toArray(Collection<T> c){ T[] ret= new T[c.size()]; // NO! errore di compilazione int i = 0 ; for (T x :c){ret[i++] = x;} return ret; }
Le variabili di tipo non sono reificabili → viene generato un generic array creation error
Cannot create a generic array of T
Non poter creare array di generics è una limitazione di java Array generici sono problematici a causa della erasure (tuttavia) erasure facilita l'evoluzione
In generale … dire “no!” ad array ed utilizzare il Collection Framework
Tramite refection è possibile ovviare al problema della creazione di array generici
public static <T> T[]toArray(Collection<T> coll){ T[] ret = (T[]) new Object[coll.size()]; // Unchecked Cast! int i = 0 ; for (T x :coll){ret[i++] = x;} return ret; } --- List<String> stringhe = ... String[] a = toArray(stringhe); // Class Cast Exception
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; at generics.cap6.Ex1.main(Ex1.java:23)
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; at generics.cap6.Ex1.main(Ex1.java:23)
public static Object[] toArray1( Collection coll){ Object[] ret = (Object[]) new Object[coll.size()]; // Unchecked Cast sparito! --> Erasure: converte il cast a T[] in un Cast a Object[] int i = 0 ; for (Object x :coll){ ret[i++] = x;} return ret; } --- List<String> stringhe = Arrays.asList("ciao","mondo"); String[] a = (String[])toArray(stringhe); //ClassCastError --> Erasure: inserisce il cast a String[]
Attenzione! Nonostante l'array contenga solamente stringhe, il suo tipo reifcato è un array di Object!
I cast inseriti dall'erasure non falliscono mai a parte quando viene generato un unchecked cast warning Quando viene generato un unchecked cast warning allora i cast inseriti dalla erasure
A volte un modo per fare soldi è tramite altri soldi. Lo stesso si può applicare agli array: generare array tramite un altro array
public static <T> T[] toArray( Collection<T> coll, T[] arr){ T[] ret = null; ret = (T[])java.lang.reflect.Array.newInstance (arr.getClass().getComponentType(), // unchecked cast coll.size()); int i = 0; for (T elem: coll) ret[i++] = elem; return ret; } }
List<String> a= Arrays.asList("ciao","a"); String[] st = toArray(a, new String[0]); for (String s: st) System.out.println(s);
public static <T> T[] toArray( Collection<T> coll, Class<T> classse){ T[] ret = null; ret = (T[])java.lang.reflect.Array.newInstance ( Classse, coll.size() ); int i = 0; for (T elem: coll) ret[i++] = elem; return ret; }
List<String> a= Arrays.asList("ciao","a"); String[] st2 = toArray(a, String.class);