Java 5 přinesla generika do jazyka Java. V tomto článku vás seznámím s generikami a pojednám o generických typech, obecných metodách, generikách a odvození typů, kontroverznosti generik a znečištění generiky a haldy.
stáhnout Získat kód Stáhněte si zdrojový kód pro příklady v tomto výukovém programu Java 101. Vytvořil Jeff Friesen pro JavaWorld.Co jsou to generika?
Generika jsou kolekce souvisejících jazykových funkcí, které umožňují typům nebo metodám pracovat na objektech různých typů a zároveň poskytují bezpečnost typu při kompilaci. Obecné vlastnosti řeší problém java.lang.ClassCastException
s je vyvolána za běhu, což je výsledek kódu, který není typově bezpečný (tj. casting objektů z jejich aktuálních typů na nekompatibilní typy).
Generics and the Java Collections Framework
Generika jsou široce používána v prostředí Java Collections Framework (formálně představeno v budoucnu) Java 101 články), ale nejsou pro ni exkluzivní. Generika se také používají v jiných částech standardní knihovny tříd Java, včetně java.lang.Class
, java.lang. Srovnatelné
, java.lang.ThreadLocal
, a java.lang.ref.WeakReference
.
Zvažte následující fragment kódu, který demonstruje nedostatek bezpečnosti typu (v kontextu rámce Java Collections Framework java.util.LinkedList
třída), která byla v kódu Java běžná před zavedením generik:
Seznam doubleList = nový LinkedList (); doubleList.add (nový Double (3.5)); Double d = (Double) doubleList.iterator (). Next ();
I když cílem výše uvedeného programu je pouze ukládání java.lang.Double
objekty v seznamu, nic nebrání tomu, aby byly uloženy jiné druhy objektů. Můžete například určit doubleList.add ("Dobrý den");
přidat a řetězec java.lang
objekt. Při ukládání jiného druhu objektu je však poslední řádek (Dvojnásobek)
vyvolá operátor obsazení ClassCastException
být hozen při konfrontaci sDvojnásobek
objekt.
Protože tento nedostatek zabezpečení typu se nezjistí až za běhu, vývojář si nemusí být vědom problému a ponechat jej na klientovi (místo kompilátoru), aby jej objevil. Generika pomáhají kompilátoru upozornit vývojáře na problém s ukládáním objektu pomocíDvojnásobek
zadejte do seznamu povolením vývojáři označit seznam jako pouze obsahující Dvojnásobek
předměty. Tato pomoc je ukázána níže:
Seznam doubleList = nový LinkedList (); doubleList.add (nový Double (3.5)); Double d = doubleList.iterator (). Next ();
Seznam
nyní zní „Seznam
z Dvojnásobek
.” Seznam
je obecné rozhraní vyjádřené jako Seznam
, to trvá a Dvojnásobek
argument typu, který je také určen při vytváření skutečného objektu. Kompilátor nyní může vynutit správnost typu při přidávání objektu do seznamu - seznam by se například mohl uložit Dvojnásobek
pouze hodnoty. Toto vynucování odstraňuje potřebu (Dvojnásobek)
obsazení.
Objevování obecných typů
A obecný typ je třída nebo rozhraní, které zavádí sadu parametrizovaných typů pomocí a seznam parametrů formálního typu, což je čárkami oddělený seznam názvů parametrů typu mezi dvojicí hranatých závorek. Obecné typy dodržují následující syntaxi:
třída identifikátor<formalTypeParameterList> Rozhraní {// class body} identifikátor<formalTypeParameterList> {// tělo rozhraní}
Rámec Java Collections Framework nabízí mnoho příkladů obecných typů a jejich seznamů parametrů (a v tomto článku na ně odkazuji). Například, sada java.util
je obecný typ, je jeho formální seznam parametrů typu a
E
je parametr osamělého typu seznamu. Dalším příkladem jejava.util.Map
.
Konvence pojmenování parametrů typu Java
Konvence programování v jazyce Java diktuje, že názvy parametrů typu musí být velká velká písmena, například A parametrizovaný typ je instance obecného typu, kde jsou parametry typu obecného typu nahrazeny argumenty skutečného typu (názvy typů). Například, Jazyk Java podporuje následující druhy argumentů skutečného typu: Každý generický typ implikuje existenci a surový typ, což je obecný typ bez formálního seznamu parametrů typu. Například, Deklarování obecného typu zahrnuje určení seznamu parametrů formálního typu a přístup k těmto parametrům typu během jeho implementace. Použití obecného typu zahrnuje předání argumentů skutečného typu do jeho parametrů typu při vytváření instance obecného typu. Viz Výpis 1. Výpis 1 ukazuje generickou deklaraci typu a použití v kontextu jednoduchého typu kontejneru, který ukládá objekty příslušného typu argumentu. Kvůli zjednodušení kódu jsem vynechal kontrolu chyb. The The Zkompilovat výpis 1 ( Všimněte si však, že v tomto příkladu neexistuje způsob, jak porušit bezpečnost typu. Jednoduše není možné uložit Vykonat The Někdy budete chtít omezit typy argumentů skutečného typu, které lze předat parametru typu. Například možná chcete omezit parametr typu tak, aby přijímal pouze Parametr typu můžete omezit zadáním parametru horní hranice, což je typ, který slouží jako horní limit pro typy, které lze předat jako argumenty skutečného typu. Určete horní mez pomocí vyhrazeného slova Například, K parametru typu můžete přiřadit více než jednu horní hranici. První vazba však musí být vždy třída a další hranice musí být vždy rozhraní. Každá vazba je oddělena od svého předchůdce znakem ampersand ( Výpis 2 The The Zkompilovat výpis 2 ( Pro parametr obecného typu nemůžete určit dolní mez. Abychom pochopili, proč doporučuji přečíst si Časté dotazy Java Generics o Angelice Langerové na téma dolních mezí, které podle ní „by byly matoucí a nijak zvlášť užitečné.“ Řekněme, že chcete vytisknout seznam objektů bez ohledu na to, zda se jedná o řetězce, zaměstnance, tvary nebo nějaký jiný typ. Váš první pokus může vypadat jako to, co je uvedeno v seznamu 3. Zdá se logické, že seznam řetězců nebo seznam celých čísel je podtypem seznamu objektů, přesto si kompilátor stěžuje, když se pokusíte zkompilovat tento výpis. Konkrétně vám řekne, že seznam řetězců nelze převést na seznam objektů a podobně pro seznam celých čísel. Chybová zpráva, kterou jste obdrželi, souvisí se základním pravidlem generik:E
pro prvek, K.
pro klíč, PROTI
pro hodnotu a T
pro typ. Pokud je to možné, nepoužívejte nesmyslné jméno jako P
— java.util.List
znamená seznam prvků, ale co byste tím mohli myslet Seznam
Soubor
je parametrizovaný typ kde Tětiva
je skutečný argument typu nahrazující parametr typu E
.Seznam
, Zvíře
je předán E
.Soubor
, Seznam
je předán E
.Mapa
, Tětiva
je předán K.
a Tětiva[]
je předán PROTI
.třída Kontejner {Nastavit prvky; }
, E
je předán E
.?
) je předán parametru typu. Například v Třída
, ?
je předán T
.Třída
je surový typ pro Třída
. Na rozdíl od obecných typů lze surové typy použít s jakýmkoli druhem objektu.Deklarace a používání obecných typů v Javě
Výpis 1:
GenDemo.java
(verze 1)class Container {private E [] elements; soukromý int index; Kontejner (velikost int) {elements = (E []) nový objekt [velikost]; index = 0; } void add (prvek E) {prvky [index ++] = prvek; } E get (int index) {návratové prvky [index]; } int size () {návratový index; }} public class GenDemo {public static void main (String [] args) {Container con = new Container (5); con.add ("sever"); con.add ("jih"); con.add ("východ"); con.add ("Západ"); for (int i = 0; i <con.size (); i ++) System.out.println (con.get (i)); }}
Kontejner
třída se deklaruje jako obecný typ zadáním seznam parametrů formálního typu. Zadejte parametr
E
se používá k identifikaci typu uložených prvků, prvku, který má být přidán do interního pole, a návratového typu při načítání prvku.Kontejner (velikost int)
konstruktor vytvoří pole pomocí elements = (E []) nový objekt [velikost];
. Pokud se divíte, proč jsem to nespecifikoval elements = new E [size];
, důvodem je, že to není možné. Mohlo by to vést k a ClassCastException
.javac GenDemo.java
). The (E[])
cast způsobí, že kompilátor vydá varování, že obsazení není zaškrtnuto. Označí to možnost, že odlidnění z Objekt[]
na E[]
může porušovat bezpečnost typu, protože Objekt[]
lze uložit jakýkoli typ objektu.E
objekt v interním poli. Předpona Kontejner (velikost int)
konstruktor s @SuppressWarnings („nezaškrtnuto“)
by potlačil tuto varovnou zprávu.java GenDemo
pro spuštění této aplikace. Měli byste dodržovat následující výstup:sever jih východ západ
Parametry vázaného typu v Javě
E
v Soubor
je příkladem parametr neomezeného typu protože můžete předat jakýkoli argument skutečného typu E
. Můžete například určit Soubor
, Soubor
nebo Soubor
.Zaměstnanec
a jeho podtřídy.rozšiřuje
následuje název typu horní meze.zaměstnanci třídy
omezuje typy, které lze předat Zaměstnanci
na Zaměstnanec
nebo podtřída (např. Účetní
). Upřesnění noví zaměstnanci
by bylo legální, zatímco noví zaměstnanci
by bylo nezákonné.&
). Podívejte se na výpis 2.Výpis 2:
GenDemo.java
(verze 2)import java.math.BigDecimal; import java.util.Arrays; abstraktní třída Zaměstnanec {private BigDecimal hourlySalary; soukromé jméno řetězce; Zaměstnanec (název řetězce, BigDecimal hourlySalary) {this.name = name; this.hourlySalary = hourlySalary; } public BigDecimal getHourlySalary () {návrat hourlySalary; } public String getName () {zpáteční jméno; } public String toString () {návratové jméno + ":" + hourlySalary.toString (); }} třída Účetní rozšiřuje Zaměstnanec implementuje Srovnatelné {Účetní (název řetězce, BigDecimal hourlySalary) {super (name, hourlySalary); } public int compareTo (Accountant acct) {return getHourlySalary (). compareTo (acct.getHourlySalary ()); }} třída SortedEmployees
Zaměstnanec
třída abstrahuje koncept zaměstnance, který pobírá hodinovou mzdu. Tato třída je podtřída Účetní
, který také implementuje Srovnatelný
to naznačit Účetní
S lze porovnat podle jejich přirozeného řádu, což je v tomto příkladu hodinová mzda.java.lang. Srovnatelné
rozhraní je deklarováno jako obecný typ s názvem jediného parametru typu T
. Toto rozhraní poskytuje int compareTo (To)
metoda, která porovnává aktuální objekt s argumentem (typu T
), vrací záporné celé číslo, nulu nebo kladné celé číslo, protože tento objekt je menší než, rovný nebo větší než zadaný objekt.Seřazení zaměstnanci
třída vám umožní ukládat Zaměstnanec
instance podtřídy, které se implementují Srovnatelný
v interním poli. Toto pole je tříděno (přes java.util.Arrays
třídy void sort (Object [] a, int fromIndex, int toIndex)
třídní metoda) vzestupně podle hodinové mzdy po an Zaměstnanec
instance podtřídy je přidána.javac GenDemo.java
) a spusťte aplikaci (java GenDemo
). Měli byste dodržovat následující výstup:George Smith: 15,20 Jane Jones: 25,60 John Doe: 35,40
Dolní hranice a parametry obecného typu
Vzhledem k zástupným znakům
Výpis 3:
GenDemo.java
(verze 3)import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class GenDemo {public static void main (String [] args) {List direction = new ArrayList (); direction.add ("sever"); direction.add ("jih"); direction.add ("východ"); direction.add ("západ"); printList (pokyny); List grades = new ArrayList (); grades.add (nové celé číslo (98)); grades.add (nové celé číslo (63)); grades.add (nové celé číslo (87)); printList (stupně); } static void printList (List list) {Iterator iter = list.iterator (); while (iter.hasNext ()) System.out.println (iter.next ()); }}