Programming Languages Hacks

Importanti regole per linguaggi di programmazione rilevanti come Java, C, C++, C#…

  • Subscribe

  • Lettori

    I miei lettori abituali

  • Twitter

Il Problema della Non-Covarianza nei Generics in Java e C#

Posted by Ricibald on 11th April 2008

Java, C# e C++ non contemplano il concetto di covarianza:

Una classe C<T> si dice covariante rispetto a T se, date due classi A e B tali che B deriva da A, risulta anche che C<B> deriva da C<A>.

Significa quindi che questa istruzione solleva un’eccezione:

List<Persona> studenti;
List<Object> objects = (List<Object>) studenti; // Errore

Questo deriva da un motivo di logica Object Oriented, non strutturale del linguaggio. Infatti il cast verso “objects” altera il comportamento di “studenti”, che accetta come elementi della lista solo oggetti che derivano da “Studente”. Se fosse stato ammesso, ciclare la collezione “studenti” avrebbe potuto restituire oggetti anche non di tipo “Studente” che non è ammesso. In altre parole, il problema è che la covarianza consente di definire sottoclassi che restringono la classe base.

Il concetto di covarianza si ripercuote sull’utilizzo dei metodi. Ad esempio: immaginiamo il metodo di una nostra implementazione della classe List:

class List<T> {
    AddAll(List<T> l);
}

Immaginiamo di avere una classe Studente che deriva da Persona:

List<Studente> studenti = new List<Studente>();
List<Persona> persone = new List<Persona>();
studenti.AddAll(studenti); // Ok
studenti.AddAll(persone);  // Errore

Il codice funziona se introduciamo wildcard (Java) o naked constraint (C#):

// Java
class List<T> {
    addAll(List<? extends T> l);
}
// C#
class List<T> {
    AddAll<U>(List<U> l) where U : T;
}

Se invece vogliamo staccarci dalla genericità e trattare solo istanze di tipo “Object” osserviamo (come abbiamo già visto) che questo non compila:

List<Object> l = (List<Object>)persone; // Errore

Ma utilizzando wildcard (Java) o collezioni raw (C#) la classe compila:

List<?> l = persone; // Java
IList l = persone;   // C#

Da cui deriva che in C#, quando realizziamo una classe generica, è necessario fornire sempre un’implementazione non generica per supportare upcast non generici:

// C#
class List {
    Add(object item);
    AddAll(List l);
}
class List<T> : List {
    new Add(T item);
    new AddAll<U>(List<U> l) where U : T;
}

Si noti infine il differente approccio Java/C#:

  • Le librerie Java forniscono un supporto al problema dell’upcast:
    • I metodi sono già predisposti (si veda addAll, che accetta in input una Collection<? extends E>)
    • Il cast verso tipi raw è di default supportato (List<?>)
  • Le librerie C# non sono pensate per il supporto verso l’upcast:
    • I metodi non prevedono naked constraint (AddAll accetta in input solo una List<T>). I naked constraint sembrano più che altro un workaround non dichiarato
    • Il cast verso tipi raw è fornito solo se viene fornita un’implementazione non generica della classe (è quindi richiesto l’intervento implementativo dello sviluppatore)

Posted in .net, java | 3 Comments »

Collezioni di Sola Lettura (Read-Only) in .NET

Posted by Ricibald on 7th February 2008

Per utilizzare collezioni readonly bisognerebbe utilizzare il metodo:

List<T>.AsReadOnly()

Avvolge la lista in modo da impedirne la modifica. Ha complessità O(1). Quindi non esegue una copia della lista, ma rende la lista puntata read-only.
Un utilizzo pratico è quindi:

class Universita
{
private IList<Studente> _studenti = new List<Studente>();

public IList<Studente> Studenti
{
get
{
return _studenti.AsReadOnly();
}
}

public void AddStudente(Studente studente) {
_studenti.Add(studente);
}
}

Risulta quindi modificabile dentro la classe, ma non modificabile per i clienti della classe. Attenzione però: per non modificabile si intende che la collezione non può essere modificata, ma le proprietà dei suoi elementi invece potrebbero essere modificate (il nome di uno studente potrebbe essere modificato).

Per questo la visibilità delle proprietà “set” deve essere opportunamente progettata, in modo che la classe Università non veda i metodi per impostare le proprietà di Studente.

Posted in .net | 3 Comments »