Параметри на типа в Java Generics

В Java има приети конвенции за именуване на generic параметри. Имената на параметрите на типа са с единични главни букви. Параметрите на генериците се именуват както следва:

Име Значение Къде се ползва
T Type общ тип
E Element колекции (List, Set и др.)
K Key ключове в Map
V Value стойности в Map
R Result резултати от функции
U Втори свободен тип когато T вече се използва

Генериците в Java могат да съдържат повече от един тип параметър. По-долу са дадени най-честите двойки и тяхната роля:

Параметри Значение Къде се ползват
K, V Key, Value Map, HashMap, TreeMap
T, U Два произволни типа двойки, сравнения, utility класове
T, R Type, Result функции и трансформации (Function)
E, T Element, Type колекции, използващи допълнителен тип
N, T Number, Type numeric utilities, математика
T, S Type, Second type (S = secondary) generics с два свободни типа


Разликата между T и U и T и S е само семантична — т.е. свързана е с значението, което програмистът влага, а не с поведение на Java:

  • “T” и “U” могат да се зползват, когато имаме два напълно независими, произволни типа. “U” означава втори свободен тип параметър (“U” идва от Unknown, Unused, second type).
  • “T” и “S” се използват, когато вторият тип има някаква концептуална връзка с първия. “S” често се чете като „Second Type“, но се използва, когато U не е подходящо, защото искаме да подсетим, че двата типа имат логическа близост.

Raw типове

Raw тип се нарича генеричен клас или интерфейс, при употребата на който не са приложени параметри за тип. Като пример можем да разгледаме генерик класа Box:

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

За да бъде създаден параметризиран тип Box, е необходимо като аргумент на формалния параметър T да се добави използвания за казуса тип:

Box<Integer> intBox = new Box<>();

Но ако аргументът, задаващ използвания тип, се пропусне, се създава raw тип на Box:

Box rawBox = new Box();

Следователно Box е raw тип на генеричния тип Box<T>. Трябва да се има предвид обаче, че обикновените класове и интерфейси не са raw тип.

Raw типове се откриват в наследен (legacy) код, тъй като много API класове (като класовете на колекциите ) не са били генерици преди JDK 5.0. Когато използвате raw типове, вие по същество получавате негенерично поведение - Box ви дава Object s. За обратна съвместимост е разрешено присвояването на параметризиран тип към неговия raw тип:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;

Но ако присвоите необработен тип на параметризиран тип, получавате предупреждение:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8);  // warning: unchecked invocation to set(T)

Предупреждението показва, че необработените типове заобикалят общите проверки за типове, като отлагат улавянето на опасен код за времето за изпълнение. Следователно трябва да избягвате използването на сурови видове.