it-swarm-eu.dev

Co se děje s generiky Java?

Na těchto stránkách jsem několikrát viděl příspěvky, které odsuzují implementaci generik v Javě. Nyní mohu upřímně říci, že jsem s jejich použitím neměl žádné problémy. Nepokusil jsem se však o generickou třídu sám. Jaké jsou vaše problémy s obecnou podporou Java?

50
Michael K

Obecná implementace Java používá vymazání typ . To znamená, že vaše silně zadané obecné kolekce jsou ve skutečnosti za běhu typu Object. To má některé aspekty výkonu, protože to znamená, že primitivní typy musí být při přidání do obecné kolekce boxovány. Výhody správnosti typu kompilace času samozřejmě převažují nad celkovou smazáním typu mazání a obsedantním zaměřením na zpětnou kompatibilitu.

56
ChaosPandion

Všimněte si, že již poskytnuté odpovědi se soustřeďují na kombinaci jazyka Java, jazyka JVM a knihovny tříd Java Knihovna tříd.

Pokud jde o jazyk Java, není nic špatného na generikách Java. Jak je popsáno v C # vs Java generics , generika Java jsou na jazykové úrovni celkem v pořádku1.

Suboptimální je, že JVM nepodporuje přímo generika, což má několik hlavních důsledků:

  • části knihovny knihoven související s odrazem nemohou odhalit úplné informace o typu, které byly k dispozici v jazyce Java-the-language
  • je zahrnuta určitá pokuta za výkon

Předpokládám, že rozdíl, který dělám, je pedantický, protože jazyk Java je všudypřítomně kompilován s JVM jako cílem a Java Knihovna tříd jako základní knihovna).

1 S možnou výjimkou zástupných znaků, o nichž se předpokládá, že je odvození typu v obecném případě nerozhodnutelné. To je hlavní rozdíl mezi generiky C # a Java), které se vůbec často nezmiňují. Díky, Antimony.

26
Roman Starkov

Obvyklou kritikou je nedostatek reifikace. To znamená, že objekty za běhu neobsahují informace o svých obecných argumentech (ačkoli informace jsou stále přítomny na polích, metodách, konstruktérech a rozšířených třídách a rozhraních). To znamená, že můžete obsadit, řekněme, ArrayList<String> Na List<File>. Kompilátor vám dá varování, ale také vás upozorní, pokud vezmete ArrayList<String>, Přiřadíte jej k odkazu Object a poté jej odevzdáte do List<String>. Nevýhodou je, že s generiky byste pravděpodobně neměli dělat casting, výkon je lepší bez zbytečných dat a samozřejmě zpětné kompatibility.

Někteří lidé si stěžují, že nemůžete přetížit na základě obecných argumentů (void fn(Set<String>) a void fn(Set<File>)). Místo toho musíte použít lepší názvy metod. Upozorňujeme, že toto přetížení nebude vyžadovat opakování, protože přetížení je statický problém s kompilací.

Primitivní typy nefungují s generiky.

Zástupné znaky a hranice jsou poměrně komplikované. Jsou velmi užitečné. Pokud je Java preferovaná neměnitelnost a rozhraní tell-not-ask), pak by byla vhodnější generika na straně deklarace než na straně použití.

13

Generici Java sají, protože nemůžete dělat následující:

public class AsyncAdapter<Parser,Adapter> extends AsyncTask<String,Integer,Adapter> {
    proptected Adapter doInBackground(String... keywords) {
      Parser p = new Parser(keywords[0]); // this is an error
      /* some more stuff I was hoping for but couldn't do because
         the compiler wouldn't let me
      */
    }
}

Nemuseli byste řezat každou třídu ve výše uvedeném kódu, aby to fungovalo tak, jak by mělo, kdyby generika byla vlastně parametry obecné třídy.

10
davidk01
  • varování kompilátoru pro chybějící obecné parametry, které neslouží žádnému účelu, činí jazyk zbytečně podrobným, např. public String getName(Class<?> klazz){ return klazz.getName();}

  • Generics nehrajte Nice s poli

  • Informace o ztraceném typu způsobují odraz nepořádku odlévání a lepicí pásky.

5
Steve B.

Myslím, že jiné odpovědi to do jisté míry řekly, ale ne zcela jasně. Jedním z problémů s generiky je ztráta generických typů během procesu reflexe. Například:

List<String> arr = new ArrayList<String>();
assertTrue( ArrayList.class, arr.getClass() );
TypeVarible[] types = arr.getClass().getTypedVariables();

Vrácené typy bohužel nemohou říct, že generické typy Arr jsou String. Je to jemný rozdíl, ale je to důležité. Protože arr je vytvořen za běhu, generické typy jsou za běhu vymazány, takže to nemůžete přijít. Jak někteří uvedli ArrayList<Integer> vypadá stejně jako ArrayList<String> z hlediska reflexe.

Pro uživatele Generics to nemusí být důležité, ale řekněme, že jsme chtěli vytvořit nějaký fantastický rámec, který by pomocí reflexe zjistil fantastické věci o tom, jak uživatel deklaroval konkrétní generické typy instance.

Factory<MySpecialObject> factory = new Factory<MySpecialObject>();
MySpecialObject obj = factory.create();

Řekněme, že jsme chtěli, aby obecná továrna vytvořila instanci MySpecialObject, protože to je konkrétní generický typ, který jsme pro tuto instanci deklarovali. Tovární třída Factory se sama nemůže dotazovat, aby zjistila konkrétní typ deklarovaný pro tuto instanci, protože Java je vymazala.

V obecných doménách .Net to můžete udělat, protože za běhu objekt ví, že jde o generické typy, protože kompilátor je splnil do binárního kódu. S vymazáním Java to nemůže udělat).

5
chubbsondubs

Mohl bych říci několik dobrých věcí o generikách, ale to nebyla otázka. Mohl bych si stěžovat, že nejsou k dispozici v době běhu a nepracují s poli, ale to bylo zmíněno.

Velká psychologická nepříjemnost: Občas se dostávám do situací, kdy není možné přimět generiky k práci. (Pole jsou nejjednodušším příkladem.) A nemohu přijít na to, zda generici nemohou dělat svou práci, nebo jestli jsem prostě hloupá. Nesnáším to. Něco jako generici by mělo fungovat vždy. Pokaždé, když nemůžu dělat to, co chci, pomocí jazyka Java, vím vím , problém je mnou a vím, jestli jen udržuji tlačí, nakonec se tam dostanu. S generiky, pokud budu příliš vytrvalý, můžu ztrácet spoustu času.

Ale skutečný problém je to, že generici přidávají příliš mnoho složitosti pro příliš malý zisk. V nejjednodušších případech mi může zabránit v přidávání Apple do seznamu obsahujícího auta. Fajn. Ale bez generiků by tato chyba hodila ClassCastException opravdu rychle za běhu s malým časem zbytečným. přidat auto s dětskou sedačkou s dítětem v něm, potřebuji varování v době kompilace, že seznam je určen pouze pro auta s dětskými sedačkami, která obsahují dětské šimpanze? Seznam jednoduchých instancí objektů začíná vypadat jako dobrý nápad.

Obecný kód může obsahovat mnoho slov a postav, zabírat spoustu dalšího prostoru a číst mnohem déle. Dokážu trávit spoustu času tím, že všechny kódy navíc fungují správně. Když se tak stane, cítím se šíleně chytrý. Ztrácel jsem také několik hodin nebo více svého času a musím se zeptat, jestli někdo jiný někdy nedokáže zjistit kód. Rád bych ji mohl předat k údržbě lidem, kteří jsou buď méně chytří než já nebo kteří mají méně času na ztrátu.

Na druhé straně (Dostal jsem se tam trochu vinutí a cítím potřebu zajistit určitou rovnováhu), je hezké, když pomocí jednoduchých sbírek a map mít přidává, staví a dostane zkontrolován, když je psán, a obvykle to nepřidává příliš složitost kódu ( pokud někdo jinde zapíše sbírku nebo mapu) . A Java je v tom lepší než C #. Zdá se, že žádná ze sbírek C #, které chci použít, nikdy nezpracovává generika. (Přiznám se, že mám ve sbírkách zvláštní chutě.)

1
RalphChapin