Jde o to, co myslíte tím hraním s polymorfismem. Já to chápu tak, že byste očekával následující význam:
ArrayList<String> al = new ArrayList<String>();
Jenže většina programátorů chce být na nějaké hrátky s polyformismem připravená, a tak očekávají
List<String> al = new ArrayList<String>();
V tom druhém případě by to ale znamenalo, že by se do popisu jazyka dostala tvrzení jako "třída java.util.ArrayList je implementací rozhraní java.util.List". A to tvůrci jazyka nechtěli.
A v cem to zhorsuje citelnost? Zapis s var je dobry tak maximalne pro privatni promene v metode. Tech se zas tak moc netvori, kamkoli jinam stejne date rozhrani misto implementace proto, aby bylo mozne nasazovat mocky a cely kod testovat. Nehlede na to typ promene urcujete az podle prirazeni coz mi prijde dost hruzne a naopak daleko hure citelne, protoze musite precist cely radek byste vedel o co vlastne jde, jinak vam staci precist pouze kus radku do znaku =.
Ok, dam priklad z praxe.
Drajver z oraclu. Vypytam si od neho prepared statement
PreparedStatement xy = connection.prepareStatement("Blabla");
Automaticky som cakal, ze byde typu OraclePreparedStatementm ale nie OraclePreparedStatement je az fieldom OraclePreparedStatementWrapper. Ok, takze reku, ze uz ho mam. Ale ono to zasa bolo typu T4CPreparedstatement, ktory dedi od OraclePreparedStatement.Tak ok, zasa som potreboval ziskat subclassu a z nej fieldy a tie az mi na nieco boli dobre.
Vsimnite si ten maglajz v predchadzajucom odstavci, zda sa to ako bluznenie, ale je to pravda .Proste potreboval som sa v tom vrtat a nejake veci si vytiahnut pomocou reflexie a zalogovat.
Ano chapem vyuzitie rozhrani v dependency injection, na ktore narazate. Predsalen tam sa to s tymi rozhraniami az tak neprehana a je to zrozumitelnejsie.
Ale vyuzivanie polymorfizmu v jave niekedy sposobuje, ze by si clovek radsej vyskriabol oko. Skratka niekedy sa veci zbytocne komplikuju, aj ked nemusia.
Ale to nie je o docitani riadku, ale o debugovani a rypani sa v neprehladnom kode.
Preto mi ten zapis, neprijde taky uleteny.
var a = new LinkedList<String>()
by znamenalo
LinkedList<String> a = new LinkedList<String>().
A co?
var a = new LinkedList<String>() // by znamenalo LinkedList<String> a = new LinkedList<String>().
imho neco takoveho v staticky typovanem jazyce nema co delat.
staci si vzit nejaky kod typu:
var foo = bar.baz();
a bez IDE clovek stravi pul dne hledanim, co to je vlastne za hodnotu.
A bez tech IDE se poserete z toho psani
LinkedList<String> a = new LinkedList<String>();
Jako opicka opisujete, opisujete a vubec to neni citelne. Jen dalsi tuna naprostu zbytecneho balastu. Nastesti jini maji mozek a pri navrhu staticky typovaneho jazyka tohle zavrhli ci zminimalizovali nutnost to psat. Doporucuji se podivat na Scalu.
A co dela clovek pul dne bez IDE? Ja casto byvam bez IDE i mnohem dele :D ale to je mi pak jedno jaka hodnota to je, protoze bez IDE proste delam neco jinyho (treba spim nebo jdu ven). Situace ze bych chtel programovat a nemel IDE samozrejme nastat muze, treba vypadek proudu, ale pak bych nemel ani ten kod.
Jinak pro lokalni promenne absolutne nema smysl resit jestli to bude List nebo krasne podle zasad LinkedList. V okamziku kdy budu potrebovat interface, tak to proste zmenim.
Ale tady resite problem navratove hodnoty a nikoli deklarace promene. To jsou dva naprosto odlisne pripady.
--
Ja bych ocekaval z tohohle DB2PreparedStatement a nekdo jiny zase ODBCPreparedStatement. A nejprve by bylo potreba se prohrabat tunou predchoziho kodu, aby clovek vlastne zjistil o jakou connection se jedna a co mu muze vracet.
--
A co by znamenalo?:
var a;
if (cokoli) {
a = LinkedList<String>();
} else {
a = HashSet<String>();
}
A jak byste pak kod refaktroval? A IDE by vam navrhovalo metody objektu podle ceho?
A co třeba se inspirovat u konkurence? Když se konkurence mohla inspirovat od Javy
void Correct()
{
var v = new List<string>();
v.Add("Test");
}
==> O.K.
void Incorrect(bool b)
{
var v;
if (b)
v = new List<string>();
else
v = new HashSet<string>();
v.Add("Test");
}
==> Error CS0818: An implicitly typed local variable declarator must include an initializer
No nevím, ale tady podle mě nejde o polymorfismus. ArrayList není potomkem Listu, ale je to jeho implementace. Tady se jedná o oddělení rozhraní od implementace.
K tomu var bych řekl, že v javě se to tak prostě nepíše a nevidím důvod proč tam cpát každý jazykový konstrukt, který existuje jinde. Na platformě Java existují jazyky kde to tak psát jde(Scala, Groovy, Jython ...), tak komu to vadí ať použije je.
Jak se mi "minulé novinky" zamlouvaly, tak tyhle zase tak moc ne. Toto se dá uspokojivě řešit něčím jako IOUtils.closeQuietly() (z Commons IO) nebo si napíšu vlastní metodu (klidně i s vyhazováním vyjímek):
void close(Closeable... objs) throws Exception { Exception ex = null; for (Closeable o : objs) { try { o.close(); } catch (Exception e) { if (ex == null) ex = e; } } if (ex != null) throw e; }
Jenze kam se tato metoda ma napsat? Do tridy, ktera s closeable objekty pracuje nebo jako staticka metoda (=funkce) do vlastni tridy? V prvnim pripade to bude copy & paste na 100 mistech ve vetsim projektu ne?
A navic to reseni v Jave (7) neni nijak genialni, to je pravda, ale zase je citelne a nestane se, ze byste zapomel na to, pridat referenci na tento objekt do vasi metody close (a to jeste na spravne misto - i kdyz tady to mate flexibilnejsi nez Java).
Jo a tusim, ze vyjimky (kdyz treba close()s vyhodi 3) se ve vasem pripade ztrati ;-)
Kdyz se neco deje "samo" neni to zrovna 2x prehledne. Jeste pochopit se to da u ruznych frameworku ale jako zaklad jazyka... nevim nevim. Ne vzdy chci volat close na vsem. Tim padem budou existovat pripady kdy nejake inicializace napisu do try() a nektere az do try{} - bude v tom jeste vetsi bordel.
Kam to napsat: nejlepe jako util tridu do java api. nebo jinam, to uz bude asi jedno.
Zapomenout se da na ledasco a ta nova syntax me nenuti ji pouzivat, takze zapomenout zavrit stream stejne muzu.
S tema vyjimkama: vyjimky se strati vzdy... nelze vyhodit 3 vyjimky najednou a nemuze to udelat ani tato specialni konstrukce. Podle logiky veci sem pochopil ze se vyhodi ta prvni a proto jsem to tak do kodu napsal. Klidne muzu vyhodit nejakou MultipleException ktera bude obsahovat seznam vyjimek...
Tohle je typicka staticka metoda do nejakeho helpru. Takze import static ... A znamena to pak volani close(opjekt) nic vic. (Btw. asi by bylo lepsi mit tu metodu lepe pojmenovanou).
P.S.: Nicmene si nemyslim, ze by pridani tohohle do jazyka byl nejaky spatny krok. Aspon je hned na startu videt co se inicializuje jako nejaky omezeny zdroj. I kdyz zase na druhou inicializujete tak neco v dobe kdyz jeste ani nemusite vedet jestli to budete potrebovat (treba vam ten kod vylitne driv nez se k pouziti daneho zdroje dostanete) v opacnem pripade se stejne nevyhnete spouste zanorenych try finally bloku.
Take si myslim, ze pridani ARM neni spatny krok. Ta inicializace az v dobe pouziti by sla mozna "obejit" nasledovne:
try ( T t1 = new T("T1"); T t2 = null; ) { ... neco, neco, neco ... t2 = new T("T2"); }
Otazka zni, zda-li se s necim takovym pocita + programator musi davat bacha na NullPointerException.
S.
Nedaří se mi rychle najít, jak to bude se zachycením výjimek při volání AutoCloseable.close()
. Bude to v některém z příštích dílů?
Z příkladů v článku je patrné, co se stane v případě, když dojde k vyvolání výjimky i v bloku try {…}
– výjimky z AutoCloseable.close()
se k té výjimce přidají jako odložené, a je pak možné si je přečíst z Throwable.getSuppressedExceptions()
(nebo se vypíšou při výpisu zásobníku volání). Ale co v případě, kdy v bloku try {…}
k výjimce nedojde, ale dojde k jedné (nebo více) výjimkám v AutoCloseable.close()
? Vyhodí se poslední výjimka z close()
, a k ní jsou případně přidány jako odložené výjimky z předchozích close()
?
A jak se to bude chovat v případě, že k vyhození výjimky dojde v bloku finally {…}
– stejně jako s try
, tj. vyhozena bude výjimka z finally {…}
a k ní budou jako odložené přidány výjimky z volání close()
? A co v případě, kdy bude výjimka vyhozena v try {…}
i finally {…}
– výjimka z try
se zahodí (jako dnes) a propaguje se jen ta z finally
s vloženými potlačenými výjimkami, nebo se i ta z try
přidá mezi potlačené? To by pak změnilo i význam kódu pro 1.6 přeloženého 1.7 kompilátorem, i když by ta změna neměla mít žádný viditelný dopad…
Vy pisete nejaky komplikovany kod. Mozno by sa niekedy hodili ale tie si mozete urobit pomocou standardneho kodu bez Closeable. Ale na 99% veci potrebujete v podstate jednoduchy handling. Nieco robim a to bud skonci OK alebo s chybou ktoru oznamym operatorovi (do logu) a skoncim.
Veci ako opakovanie operacie po N minutach a podobne sa riesia na vyssej urovni ako try/catch blok.
Uniká mi souvislost vašeho komentáře s tím mým. Já jsem neuváděl žádný příklad kódu, ptal jsem se, jak je ve standardu nadefinováno chování toho rozšířeného příkazu try
. V článku autor poukázal na to, že může v kódu vzniknout paralelně několik výjimek, a je tam uveden příklad, jak se to řeší v určitém případě – a já jsem se zeptal na ostatní případy. Do Javy 1.6 mohlo několik výjimek „najednou“ vzniknout jen vyhozením výjimky ve finally a platilo pravidlo „poslední vyhrává“. V Javě 1.7 se tohle evidentně změní, tak jsem se jen ptal, zda Pavel Tišnovský (nebo někdo jiný) náhodou neví detaily.
Je to vyresene tak, ze cely "novy" blok try, tj. blok s deklaracni casti, muze vyhodit az _n_+1 vyjimek, kde _n_ je pocet objektu vytvarenych v deklaracni casti bloku try, tj. za kulatymi zavorkami (a nic jineho v teto casti bloku byt nesmi).
Presneji - jednu vyjimku muze vyhodit telo bloku try, coz je logicke a dalsich _n_ vyjimek muze vyhodit metoda close() - ta mimochodem je definovana tak, ze vyhazuje vyjimku typu Exception ne IOException, to jsem zapomel v clanku zminit.
Seznam vyhozenych vyjimek se v catch() da zjistit pomoci getSuppressedExceptions() a navic se printStackTrace() chova ponekud jinak - vypise vsechny vyhozene vyjimky s prefixem suppressed. Je to v clanku uvedeno, prozatim bez vetsiho komentare, protoze se mozna jeste bude format menit.
Ano, chybové stavy každého otravují, proto mívají aplikace centrální exception handler, který se o to stará.
Myšlenka checked exceptions nebyla špatná, jen se postupem vývoje ukázalo, že je zbytečně paranoidní. Unchecked exceptions stačí, jako v C#.
A k té druhé části - možná by vám ještě víc chránilo kejhák to, kdybyste všude psal 'catch (NullPointerException npe) {...}' :-)
To je ovsem pusta teorie. Pokud mate dotaz na DB jako "SELEKT * FORM tabule", tak to je chyba programatora (2 preklepy) a vyjimka je stejna jako kdyz zdechne DB server. To by az tak nevadilo, ale checkovany vyjimky mnohem vic prudi nez pomahaji. Myslenka je to dobra, ale nedotazena, coz je videt uz na prikladu toho Closeable. Namisto dopisovani throws klausuli by to chtelo statickou analyzu celeho kodu s moznosti deklarovat vyjimky mimo kod tridy, tim by se daly podchytit i nektery RuntimeException-s.
stejně tomu budou všichni říkat: http://cs.wikipedia.org/wiki/P%C3%AD%C4%8Da
(pevně doufám, že odkaz na wikipedii nebude považován za vulgarismus:)
"...ke třídě implementující rozhraní Closeable (a tím pádem i metodu close) postačuje přidat další rozhraní AutoCloseable."
Dle API na java.net je java.io.Closeable subinterface java.lang.AutoCloseable, takže by citované ani nemuselo být potřeba.
http://download.java.net/jdk7/docs/api/java/io/Closeable.html
No promiňte, ale jak koukám na tu diskuzi tak si nikdo i včetně autora článku nikdo nevšiml (tragikomedie je že to neví i lidi z coinu:-( ) že v části 5. Je operátor <> skutečně nezbytný a/nebo dostatečně obecný?
má chybu a to takovou že lze zapsat... viz jednoduchý test:
//kompilováno pod jdk6, javac kompilátor pouze upozorní na potenciální //unsafe operaci
public class CollectionTest {
@Test
public void testType() {
List<String> list = new ArrayList();
List<List<String>> list2 = new ArrayList();
List<List<List<String>>> list3 = new ArrayList();
Map<String, Collection<Integer>> map = new LinkedHashMap();
//list.add(new Integer(1));//nejde zkompilovat
list.add(String.valueOf("hello"));//toto jo
//list2.add(new Integer(2));//nejde zkompilovat
list2.add(new ArrayList());//tohle jo ! všimně te si že se nekontroluje typ !!!
List beztypovyList = list;
beztypovyList.add(new Integer(3)); //tohle projde i s tím pitomým diamantem!!!
//tohle projde
for (Object object : beztypovyList) {
System.out.println(object);
}
//tohle NEprojde ale spadne ! a spadne to i s tím pitomým diamantem!!!
for (String object : list) { //java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
System.out.println(object);
}
}
}
Ano, to prece plyne z toho, ze seznamy atd. jsou v bajtkodu vzdy tvoreny jako kolekce obecny objektu (Object), je to dokonce o par odstavcu vys vypsano (je tam volani konstruktoru z bajtkodu + metody add a get).
To znamena, ze prekladac muze v nekterych pripadech zkontrolovat tyto manipulace (vypise warning), ale pretypovani se objevi az v runtime.
Hmm mozna jsme se nepochopili: operator diamantu pouze slouzi k tomu, aby se na prave strane prirazeni pri konstrukci objektu (a nikde jinde) nemusel udavat genericky typ, nic jineho. Tudiz pokud uz puvodni varianta: List<String> list = new ArrayList<String>() povolovala typove nespravne operace (jako ze povoluje), diamant to nikak nevylepsi.
Ano to jsem psal Filipe, ale to mi pořád přijde zbytečné přidávat konstrukci navíc (lze udělat pouze u vytvoření nového objektu) místo aby u vytvoření nového objektu zrušili to upozornění! ;-)
Pokud si vygeneruješ class soubor tak v obou případech bude stejný takže se se to opravdu redukuje na výběr mezi zrušení upozornění nebo přidávat novou konstrukci
tak jediný důvod který jsem našel je že pokud
List<String> strings = new ArrayList();
strings.add("hello");
//přidělím při vytvoření nové kolekce
List<Integer> integers = new ArrayList(strings);
//tak dál už to je potenciálně nebezpečné ale pokud udělám
List<Integer> integers = new ArrayList<>(strings);
//tak mi to kompilátor nedovolí