Breviar 12

1. Genericitate

1.1 Introducere

Tipurile generice simplifică lucrul cu colecții, permițând tipizarea elementelor acestora. Definirea unui tip generic se realizează prin specificarea între paranteze unghiulare a unui tip de date Java, efectul fiind impunerea tipului respectiv pentru toate elementele colecției.

În cazul folosirii tipurilor generice, încercarea de a utiliza în cadrul unei colecții a unui element necorespunzător ca tip va produce o eroare la compilare, spre deosebire de varianta în care tipurile generice nu sunt utilizate, ce permite doar aruncarea unor excepții de tipul ClassCastException.

1.2 Definirea structurilor generice

class Association<K, V> {
    private K key;
    private V value;
 
    public Association(K key, V value) {
        this.key = key;
        this.value = value;
    }
 
    public K getKey() {
        return this.key;
    }
 
    public V getValue() {
        return this.value;
    }
}

Sintaxa <K, V> este folosită pentru a defini tipuri formale în cadrul definiției clasei. Aceste tipuri pot fi folosite în mod asemănător cu tipurile uzuale. În momentul în care invocăm efectiv o structură generică, ele vor fi înlocuite cu tipurile efective.

class Test {
    public static void main(String args[]) {
        Association<String, String> map1;
        map1 = new Association<String, String>("CC", "POO");
        String s1 = map1.getKey();
 
        Association<String, Integer> map2;
        map2 = new Association<String, Integer>("POO", 2015);
        String s2 = map2.getKey();
        int nr = map2.getValue();
    }
}

1.2.1 Genericitatea în subtipuri

class Test {
    public static void main(String args[]) {
        Association<String, Integer> map1;
        // Operația 1
        map1 = new Association<String, Integer>("POO", 2015);
        Association<Object, Object> map2;
        // Operația 2
        map2 = map1;  // EROARE!
    }
}

Dacă CType este un subtip (clasă descendentă sau subinterfață) al lui PType, atunci o structură generică GenericStructure<CType> NU este un subtip al lui GenericStructure<PType>. Atenție la acest concept, întrucât el nu este intuitiv!

1.2.2 Restricționarea parametrilor

Există cazuri în care dorim să adăugăm într-o structură de date generică elemente care au un tip cu anumite proprietăți.

abstract class AVector<E extends Number> extends AbstractList<E> {
    abstract public boolean add(E obj);
    abstract public E get(int index);
    abstract public Enumeration<E> elements();
    abstract public Iterator<E> iterator();
    abstract public ListIterator<E> listIterator();
}

Sintaxa <E extends Number> indică faptul că tipul E este o subclasă a lui Number (sau chiar Number). Această restricție face imposibilă instanțierea unui obiect care să conțină elemente de tip String, deoarece String nu este o subclasă a lui Number.

Cuvântul cheie extends este folosit și în cazul în care dorim să indicăm un tip ce implementează o anumită interfață:

class Map<K extends Number, V extends Set<K>> extends Association<K, V> {
    public Map(K key, V value) {
        super(key, value);
    }
 
    public static void main(String args[]) {
        Map<Double, TreeSet<Double>> obj;
        TreeSet<Double> v = new TreeSet<Double>();
        v.add(new Double(2.5));
        obj = new Map<Double, TreeSet<Double>>(1.2, v);
        Double x = obj.getKey();
        System.out.println(x);
        TreeSet<Double> set = obj.getValue();
        System.out.println(set);
    }
}

1.3 Wildcards

Wildcard-urile sunt utilizate atunci când dorim să întrebuințăm o structură generică drept parametru într-o funcție și nu dorim să limităm tipul de date din colecția respectivă.

// Fără wildcard - restrictiv (doar Collection<Object>)
void boolean containsAll(Collection<Object> c) {
    for (Object e : c) {
        if (!this.contains(e)) {
            return false;
        }
    }
    return true;
}
 
// Cu wildcard - flexibil (orice Collection)
void boolean containsAll(Collection<?> c) {
    for (Object e : c) {
        if (!this.contains(e)) {
            return false;
        }
    }
    return true;
}

O limitare care intervine este că NU putem adăuga elemente arbitrare într-o colecție cu wildcard-uri.

class TestClass {
    public static void main(String args[]) {
        // Operație permisă
        Collection<?> c = new LinkedList<Integer>();
        // Eroare la compilare!
        c.add(new Object());
    }
}

Singurul element care poate fi adăugat este null, întrucât acesta este membru al oricărui tip referință. Operațiile de tip getter sunt posibile, întrucât rezultatul acestora poate fi mereu interpretat drept Object.

1.4 Bounded Wildcards

Mecanismul bazat pe Bounded Wildcards permite introducerea unor restricții asupra tipurilor ce pot înlocui un wildcard.

  • <? extends Number> - tipul trebuie să fie Number sau o subclasă a acesteia
  • <? super AbstractMethodError> - tipul trebuie să fie AbstractMethodError sau o superclasă
class TestClass {
    public static void printType(Set<? extends Number> set) {
        for (Number item : set) {
            System.out.println(item.getClass());
        }
    }
 
    public static void main(String args[]) {
        Set<Number> set = new HashSet<Number>();
        set.add(new Integer(5));
        set.add(new Double(7.2));
        set.add(new Float(10.5));
        TestClass.printType(set);
    }
}

1.5 Metode generice

Java ne oferă posibilitatea scrierii de metode generice (având un tip-parametru) pentru a facilita prelucrarea unor structuri generice.

class TestClass {
    // Metoda 1 - CORECTĂ
    public <T> void metoda1(T[] a, Collection<T> c) {
        for (T o : a) {
            // Operație permisă
            c.add(o);
        }
    }
 
    // Metoda 2 - EROARE la compilare
    public void metoda2(Object[] a, Collection<?> c) {
        for (Object o : a) {
            // Eroare la compilare!
            c.add(o);
        }
    }
}

Exemplu cu bounded wildcards în metode generice:

class TestClass {
    List<Number> dest;
 
    public <T> void copy(Set<T> dest, List<? extends T> src) {
        for (T o : src) {
            dest.add(o);
        }
    }
 
    public <T extends Number> boolean addAll(Collection<T> c) {
        for (T o : c) {
            dest.add(o);
        }
        return true;
    }
}

2. Decorator

2.1 Introducere

De multe ori în cadrul unei aplicații apare următorul scenariu: aveți de implementat o anumită funcționalitate care trebuie să poată fi extinsă fără să știți în momentul proiectării în ce mod va fi extinsă.

Exemplu: Lucrați la un joc de tipul shooter. Aveți o listă de arme pe care un user le poate achiziționa și utiliza. La un moment dat observați că încasările din joc încep să scadă odată ce alternativele de pe piață oferă mai multe feature-uri pentru armele lor.

Rezolvarea: Creați o clasă abstractă wrapper peste o instanță de armă. Dacă de exemplu vreți ca toate armele voastre să poată fi mortale dar silențioase, creați clasa WeaponSilencer ce implementează interfața de bază a armelor și are un obiect de tip armă intern. În metoda fire, în decorator, setați eventual sunetul jocului mai încet.

2.2 Exemplu cu fluxuri Java

CipherOutputStream cos = new CipherOutputStream(new FileOutputStream("file"), c);
PrintWriter pw = new PrintWriter(new OutputStreamWriter(cos));
pw.println("Stand and unfold yourself");
pw.close();
  • La bază avem un flux de ieșire care va scrie în fișierul “file” niște octeți
  • CipherOutputStream este o clasă decorator și adaugă peste fluxul de octeți proprietatea că acesta este criptat
  • OutputStreamWriter și PrintWriter sunt decoratoare care ne permit să adăugăm text în fluxul de ieșire

2.3 Structura Decorator

Participanți:

  • Component - definește interfața pentru obiectele care pot avea responsabilități adăugate dinamic (metoda Operation())
  • ConcreteComponent - definește un obiect căruia i se pot adăuga responsabilități suplimentare
  • Decorator - menține o referință către un obiect Component și definește o interfață conformă cu interfața lui Component (delegă apelul către component→Operation())
  • ConcreteDecoratorA - adaugă stare suplimentară (addedState) componentei
  • ConcreteDecoratorB - adaugă comportament suplimentar (AddedBehavior()) și apelează Decorator::Operation()

Atenție: Dacă în tot lanțul de decorări nu există un decorator care să aibă la bază o componentă concretă, construcția nu va funcționa.

poo/breviare/breviar-12.txt · Last modified: 2026/01/12 08:16 by george.tudor1906
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0