Reificazione, si è una parola Italiana …
Defnizioni di reifcazione
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
Unchecked cast
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
In java un tipo è reifcabile se il tipo è completamente identifcabile a runtime, ovvero se la type erasure non rimuove informazioni utili
Tipi reifcabili
- Primitivi es: int
- Classi o interfacce non parametriche es: String, Comparable
- Tipi parametrici in cui tutti gli argomenti di tipo sono wildcard non limitati es: List<?> o Map<?,?>
- Raw type es: List, Map Array di elementi di tipo reifcabile es: int[] , List<?>[]
Tipi non riefcabili
- Variabili di tipo es: T in Xxxx<T>
- Tipi parametrici con parametri attuali es: List<String>, Comparable<Integer>
- Tipi parametrici con un limite es: List<? extends String> , Comparable <? super Number>
Note
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
Instance test e cast
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;
Equals su liste: analisi
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()){ ...
Equals su liste: versione fxata
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;
Analisi
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
Cast non reifcabili
Test di istanza verso tipi non reifcabili generano sempre errore In alcune circostanze un cast ad un tipo non reifcabile può non generare warning
Nessun 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
Array e reifcazione
Gli array reifcano i loro componenti ovvero conservano a runtime i tipi dei loro componenti
Un vecchio esempio
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
Creazione di 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
Difetti ...
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
Refection ...
Tramite refection è possibile ovviare al problema della creazione di array generici
Dont' try this at home
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)
Messaggio oscuro
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)
- L signifca array di reference type
- [LObject signifca array di Object, Object è il component type dell'array
- L'errore non viene segnalato nel punto in cui viene generato (!) ma in un altro punto (!!!) ovvero nel main
Versione generata dall'erasure: analisi
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!
Cast iron guarantee (ancora)
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
- Possono fallire
- Possono essere segnalati in parti del codice diferenti da quella che ha generato l'errore
Adoro i soldi che generano i soldi
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; } }
Esecuzione del codice
List<String> a= Arrays.asList("ciao","a"); String[] st = toArray(a, new String[0]); for (String s: st) System.out.println(s);
- Ok! Funziona!
- Il metodo getCoponentType() restituisce un “Class Object” che rappresente il tipo dei componenti dell'Array T
- Class<?> ctype = arr.getClass().getComponentType(); Nel nostro caso è String
La versione "di classe"
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);