Java - rozhraní, dědičnost a abstraktní třídy

Kolemjdoucí

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #45 kdy: 17. 07. 2013, 13:41:47 »
resit to nativne = transformovat bytecode je nejrychlejsi (pri behu), je to opravdu takove prekvapeni? (navic si ani nejsem jistej, jestli by jen pomoci reflexe vse slo resit.)

Šťourat za běhu do bytecode je nepochybně nejrychlejší, ale taktéž je to důkaz že v jazyku něco chybí, proto je k smíchu že to používají přímo v baště Javy, místo toho aby si to za ty roky dodělali podporu rovnou do Javy.


noef

  • *****
  • 897
    • Zobrazit profil
    • E-mail
Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #46 kdy: 17. 07. 2013, 14:03:22 »
resit to nativne = transformovat bytecode je nejrychlejsi (pri behu), je to opravdu takove prekvapeni? (navic si ani nejsem jistej, jestli by jen pomoci reflexe vse slo resit.)

Šťourat za běhu do bytecode je nepochybně nejrychlejší, ale taktéž je to důkaz že v jazyku něco chybí, proto je k smíchu že to používají přímo v baště Javy, místo toho aby si to za ty roky dodělali podporu rovnou do Javy.

Java se rozhodla jit cestou zpetne kompatibility a imo tohle jsou nasledky. Myslim, ze treba Scala je mnohem lepsi jazyk a bylo by krasne, kdyby brzo nahradila Javu (utopie :-\). Tolik veci co v Jave chybi oproti konkurencnimu C#, pritom snad vsechno (v nejake podobe) Scala obsahuje...

perceptron

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #47 kdy: 17. 07. 2013, 15:39:03 »
Citace
V baště Javy Springu a Hibernate se manipuluje s bytecode ?
cglib

za vsetko:
1. pri proxovany konkretnych tried nestaci java.lang.proxy, preto sa cglibuje. tym sa realizuje napr. aop
2. zistovanie nazvov parametrov v metodach - kedze reflexiou sa nemozno pytat na nazvy parametrov (tusim az od jdk8), bajtkod manipulaciou sa pozera do zakompilovanych nazvov parametrov.

dovody, preco je to tak, vysvetlil kolega vyssie: v jave je spatna kompatibilita nadovsetko.

mozete si vymenit plienku :-)

moze sa okolo toho rantovat do bezvedomia a kritizovat performance, ale funguje to dlhodobo a v praktickych projektoch som nejak nevidel, zeby to bolo az take pomale, aby nad tym bolo treba ronit krvave slzy.

Svetozar Bludna

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #48 kdy: 17. 07. 2013, 15:50:09 »
Citace
Ale p*ču!
mozete to elaborovat?

Jediný problém je v tom, že nepřipouštíš zavedení nové vazby v potomkovi za přípustné, čehož důvod mi uniká. Kdybys myslel v intencích nějakého kultivovaného jazyka, jako třeba LISPu, nebo kdybys si prostě představil, že AxMinor a AxMaior nejsou obyčejné proměnné, ale obyčejné ukazatele, tak bys v konstruktoru kružnice jen zavedl vazbu AxMinor := AxMaior a rázem máš z elipsy kružnici. Že setMinor nastaví i AxMaior není nic nekorektního, protože z definice má kružnice obě poloosy stejné. Žádný logický fail to není a návrhový taky ne, neboť jde o podpřípad, s kterým elipsa samozřejmě počítá. Parametry elipsy nejsou žádným odkládacím prostorem pro dvě čísla, jsou to interní parametry objektu, co do nich bude uloženo resp. z nich přečteno záleží na interní definici třídy, uživateli po tom nic není a dokonce ani uživatel třídy elipsa nesmí spoléhat na to, že po setnutí jejích parametrů z nich getne ty samé parametry! Mohlo dojít k jejich limitaci, zaokrouhlení apod., v případě kružnice k jejich vzájemnému svázání.
Návrhová chyba je deklarovat, že nastavení hlavní poloosy nesmí mít žádný vliv na poloosu vedlejší. Proč? To je úplně zbytečná podmínka, vnitřní záležitost třídy elipsa. Setnutím požádám o nastavení takovýchto parametrů, getnutím získám reálně nastavené parametry.

Juro

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #49 kdy: 17. 07. 2013, 16:22:54 »
Tomuto foru chyba administrator ako sol. Keby mazal alebo aspon oznacoval vsetky prispevky, ktore neodpovedaju na povodnu otazku, bola by tu toho tak 1/4. A hodnotu by to malo 4x vyssiu.


perceptron

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #50 kdy: 17. 07. 2013, 17:58:28 »
snazim sa to pochopit, ale bohuzial kultivovany lisp ma nik nenaucil.

mam dve objasnujuce otazky
1) tie prepodmienky a postpodmienky samozrejme mozem uplne vypustit, ale tu mi zase nie je jasne, ake racio v tej metode potom ostane, resp. je to uz potom vec dizajnu a dohody. vam sa moze zdat, ze to je zbytocna podmienka, mne ako hypotetickemu dizajnerovu inkscapu sa moze zdat, ze ak taham za lavy/pravy cierny stvorcek, tak sirka sa meni a vyska nie.
2) aku vyhodu potom bude mat elipsu a kruznicou vztah dedicnosti? v com to potom pomoze?

(samozrejme nepopieram, ze je povinnost getnut rovnaku vec ako sa setla)

Radek Miček

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #51 kdy: 17. 07. 2013, 18:45:57 »
Citace
Ale p*ču!
mozete to elaborovat?

Jediný problém je v tom, že nepřipouštíš zavedení nové vazby v potomkovi za přípustné, čehož důvod mi uniká. Kdybys myslel v intencích nějakého kultivovaného jazyka, jako třeba LISPu, nebo kdybys si prostě představil, že AxMinor a AxMaior nejsou obyčejné proměnné, ale obyčejné ukazatele, tak bys v konstruktoru kružnice jen zavedl vazbu AxMinor := AxMaior a rázem máš z elipsy kružnici. Že setMinor nastaví i AxMaior není nic nekorektního, protože z definice má kružnice obě poloosy stejné. Žádný logický fail to není a návrhový taky ne, neboť jde o podpřípad, s kterým elipsa samozřejmě počítá. Parametry elipsy nejsou žádným odkládacím prostorem pro dvě čísla, jsou to interní parametry objektu, co do nich bude uloženo resp. z nich přečteno záleží na interní definici třídy, uživateli po tom nic není a dokonce ani uživatel třídy elipsa nesmí spoléhat na to, že po setnutí jejích parametrů z nich getne ty samé parametry! Mohlo dojít k jejich limitaci, zaokrouhlení apod., v případě kružnice k jejich vzájemnému svázání.
Návrhová chyba je deklarovat, že nastavení hlavní poloosy nesmí mít žádný vliv na poloosu vedlejší. Proč? To je úplně zbytečná podmínka, vnitřní záležitost třídy elipsa. Setnutím požádám o nastavení takovýchto parametrů, getnutím získám reálně nastavené parametry.

Podtyp ve smyslu LSP to stejně nebude.

Natix

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #52 kdy: 17. 07. 2013, 19:16:36 »
Citace
Ale p*ču!
mozete to elaborovat?

Jediný problém je v tom, že nepřipouštíš zavedení nové vazby v potomkovi za přípustné, čehož důvod mi uniká. Kdybys myslel v intencích nějakého kultivovaného jazyka, jako třeba LISPu

Já nejsem odborník na různé druhy Lispu, ale mám pocit, že je v nich vyžadováno anebo alespoň je dobrým zvykem používat neměnné datové struktury. Ve chvíli, kdy kružnice i elipsa budou immutable, tak zmiňovaný problém přestává existovat, protože zavoláním setteru (což už vlastně nebude v podtstě setter) neměním původní objekt, ale dostávám nový se změněnými vlastnostmi. Čili dostanu něco ve stylu:

Kód: [Vybrat]
class Ellipsis {
  private final int x;
  private final int y;

  int getX() {
    return x;
  }
  int getY() {
    return y;
  }
 
  Ellipsis withX(int x) {
    return new Ellipsis(x, this.y);
  }

  Ellipsis withY(int y) {
    return new Ellipsis(this.x, y);
  }

}

class Circle extends Ellipsis {
  private final int radius;

  Circle(int radius) {
    this.radius = radius;
  }

  getRadius() {
    return radius;
  }

  Circle withRadius(int radius) {
    return new Circle(radius);
  }
}

Parametry elipsy nejsou žádným odkládacím prostorem pro dvě čísla, jsou to interní parametry objektu, co do nich bude uloženo resp. z nich přečteno záleží na interní definici třídy, uživateli po tom nic není a dokonce ani uživatel třídy elipsa nesmí spoléhat na to, že po setnutí jejích parametrů z nich getne ty samé parametry! Mohlo dojít k jejich limitaci, zaokrouhlení apod., v případě kružnice k jejich vzájemnému svázání.
Návrhová chyba je deklarovat, že nastavení hlavní poloosy nesmí mít žádný vliv na poloosu vedlejší. Proč? To je úplně zbytečná podmínka, vnitřní záležitost třídy elipsa. Setnutím požádám o nastavení takovýchto parametrů, getnutím získám reálně nastavené parametry.

Sorry, ale s tímhle nejde souhlasit. Když elipse nastavím hodnotu dané poloosy na zvolenou hodnotu, tak samozřejmě je plně očekávatelné, že následným gettem tu samou hodnotu dostanu. Samozřejmě tu může docházet k nějakým zaokrouhlením nebo číselným konverzím, ale to je už je detail. Pokud setter bere int a getter vrací int, tak samozřejmě cokoliv jiného než dostat zpátky nastavenou hodnotu nedává smysl. Takhle by sis to představoval?

Kód: [Vybrat]
class Ellipsis {

  /**
   * Sets the size of X axis to the given value. Or perhaps doesn't.
   * Or sometimes it also sets the value of Y axis.
   */
  void setX(int x);

  /**
   * Returns the size of X. Or possibly some other unspecified value. Try your luck.
   */
  int getX();
}

To je jako kdybys přidal prvek do seznamu a on tam pak nebyl. Anebo jsi nastavil jméno nějaké Person beaně a ona měla následně jméno jiné. To nejsou žádné vnitřní záležitosti, to je otázka komunikace s klientem mé třídy. Pokud kontakt třídy specifikuješ podobně nelogickým a způsobem, který neoodpovídá řekněme selskému rozumu, tak jsi blázen. A pokud ho nespecifikuješ vůbec, tak nejsi programátor, ale věštec z kávového lógru.

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #53 kdy: 17. 07. 2013, 19:17:08 »
resp. je to uz potom vec dizajnu a dohody. vam sa moze zdat, ze to je zbytocna podmienka, mne ako hypotetickemu dizajnerovu inkscapu sa moze zdat, ze ak taham za lavy/pravy cierny stvorcek, tak sirka sa meni a vyska nie.
Urcite nekdo zase bude kvilet, ale neda mi to: Ten problem je presne v tom, co jsem psal - snaha koncepty OOP povazovat za "obecne platne". Tady koncept OOP "specialni pripady obecnejsich konceptu maji vsechny vlastnosti svych obecnejsich rodicu plus nejake navic" proste koliduje s poznatky z reality "kruh je specialni pripad elipsy".

A co se tyce toho inkscapu, proste budes muset pocitat s tim, ze kdyz natahnes kruh jednim smerem, vznikne ti elipsa. Coz je dalsi pripad toho, co jsem rikal: kdyby nebyli lidi zpovykani z C++, byli by zvykli na to, ze metody vraceji instance a neni nikde receno, ze to musi byt stejna instance stejne tridy. Takze kruh.setX(x) by vratilo elipsu a elipsa.setX(x).setY(x) by vratilo opet kruh. A puvodni instance by zmizela v propadlisti zapomneni :)

Svetozar Bludna

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #54 kdy: 17. 07. 2013, 19:23:06 »
snazim sa to pochopit, ale bohuzial kultivovany lisp ma nik nenaucil.

mam dve objasnujuce otazky
1) tie prepodmienky a postpodmienky samozrejme mozem uplne vypustit, ale tu mi zase nie je jasne, ake racio v tej metode potom ostane, resp. je to uz potom vec dizajnu a dohody. vam sa moze zdat, ze to je zbytocna podmienka, mne ako hypotetickemu dizajnerovu inkscapu sa moze zdat, ze ak taham za lavy/pravy cierny stvorcek, tak sirka sa meni a vyska nie.
2) aku vyhodu potom bude mat elipsu a kruznicou vztah dedicnosti? v com to potom pomoze?

(samozrejme nepopieram, ze je povinnost getnut rovnaku vec ako sa setla)

ad 1. Pokud to budete chtít udělat s kružnicí, tak logičtější chování je, že výška se změní stejně jako šířka, resp. naopak. V případě odvození kružnice od elipsy je toto zaručeno jaksi samosebou, bez jediného písmenka kódu navíc.
ad 2. Pomineme-li výhodu popsanou v předchozím bodě, pak další přirozenou výhodou je např. recyklace vykreslovacího algoritmu.

Mě by spíš zajímalo, jaké výhody má v objektovém návrhu tvařit se, že kružnice nemá s elipsou nic společeného.

Radek Miček

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #55 kdy: 17. 07. 2013, 19:31:41 »
Citace
Mě by spíš zajímalo, jaké výhody má v objektovém návrhu tvařit se, že kružnice nemá s elipsou nic společeného.

Nahraditelnost - tam, kde je potřeba instance typu T, můžete dát instanci jeho podtypu S. Pokud dodržujete LSP, tak tím neovlivníte korektnost programu.

perceptron

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #56 kdy: 17. 07. 2013, 21:23:18 »
prekvapivo, wikipedia dava velmi pekny priklad vratane rieseni (a to dokonca so spomenutim lispu, kde je mozna zmena triedy).

http://en.wikipedia.org/wiki/Circle-ellipse_problem

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #57 kdy: 17. 07. 2013, 21:45:45 »
(a to dokonca so spomenutim lispu, kde je mozna zmena triedy).
Neznam Lisp a uz vubec ne jeho objektovy system, ale tohle mi zrovna prave prislo jako dost prasacky reseni, aby se pod rukama menil typ jednoho objektu. A pritom se to da resit pekne funkcionalne - proste vratit jinej objekt... Nevim, proc tady lispisti od funkcionalniho pristupu odbocili.

Svetozar Bludna

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #58 kdy: 18. 07. 2013, 01:31:32 »
To je jako kdybys přidal prvek do seznamu a on tam pak nebyl. Anebo jsi nastavil jméno nějaké Person beaně a ona měla následně jméno jiné. To nejsou žádné vnitřní záležitosti, to je otázka komunikace s klientem mé třídy. Pokud kontakt třídy specifikuješ podobně nelogickým a způsobem, který neoodpovídá řekněme selskému rozumu, tak jsi blázen. A pokud ho nespecifikuješ vůbec, tak nejsi programátor, ale věštec z kávového lógru.

Pokud přidávám prvek do konstantního seznamu a on tam pak není, pak se není čemu divit. Stejně jako by se nemělo být čemu divit, pokud např. zadávám parametry elipsy, jež nejsou povoleny v dané situaci, nebo se snažím nastavit hlavní a vedlejší poloosu kružnice na různé hodnoty.
Já se naopak výše popsaným způsobem snažím selský rozum neopouštět výměnou za rigidní výklad objektového modelu, který dědění chápe jen jako extenzi, což je právě ve sporu se selským rozumem. Jestliže objektu žárovka pošlu zprávu, aby se rozsvítila, ale ona je prasklá, tak nemůžu čekat, že obdržím stav, že je rozsvícena, když jsem ho tam přece jasně nastavoval. S tou elipsou je to stejné.

Citace
Mě by spíš zajímalo, jaké výhody má v objektovém návrhu tvařit se, že kružnice nemá s elipsou nic společeného.

Nahraditelnost - tam, kde je potřeba instance typu T, můžete dát instanci jeho podtypu S. Pokud dodržujete LSP, tak tím neovlivníte korektnost programu.

Já chtěl nějaký argument pro podporu nepříbuzenského vztahu elipsy a kružnice, ne k podpoře toho, co jsem napsal. :-) Ano, v případě odvození kružnice od elipsy ji můžete použít na jejím místě, což může mít větší či menší smysl, ale není to principiálně špatně.

(a to dokonca so spomenutim lispu, kde je mozna zmena triedy).
Neznam Lisp a uz vubec ne jeho objektovy system, ale tohle mi zrovna prave prislo jako dost prasacky reseni, aby se pod rukama menil typ jednoho objektu. A pritom se to da resit pekne funkcionalne - proste vratit jinej objekt... Nevim, proc tady lispisti od funkcionalniho pristupu odbocili.
Doporučuji k prostudování jako zajímavost. Totálně odlišný pohled na OOP, jaký představuje CLOS (Common Lisp Object System), může být pro otevřené hlavy v lecčems inspirující. Vracení jiného objektu je naprosto běžnou záležitostí v objektově velmi čistém Smalltalku. Funguje to dobře a spolehlivě a nikdo se nad tím vůbec nepozastavuje, skoro bych řekl, že o tom ani neuvažuje a bere to jako tu nejpřirozenější věc v programování. "678 asString" prostě vrátí string, ačkoli jde o objekt typu integer, vezmu-li ten nejprimitivnější příklad.

Natix

Re:Java - rozhraní, dědičnost a abstraktní třídy
« Odpověď #59 kdy: 18. 07. 2013, 03:04:20 »
Pokud přidávám prvek do konstantního seznamu a on tam pak není, pak se není čemu divit. Stejně jako by se nemělo být čemu divit, pokud např. zadávám parametry elipsy, jež nejsou povoleny v dané situaci, nebo se snažím nastavit hlavní a vedlejší poloosu kružnice na různé hodnoty.
Já se naopak výše popsaným způsobem snažím selský rozum neopouštět výměnou za rigidní výklad objektového modelu, který dědění chápe jen jako extenzi, což je právě ve sporu se selským rozumem. Jestliže objektu žárovka pošlu zprávu, aby se rozsvítila, ale ona je prasklá, tak nemůžu čekat, že obdržím stav, že je rozsvícena, když jsem ho tam přece jasně nastavoval. S tou elipsou je to stejné.

Pokud budu mít list, který bude obsahovat metody pro modifikaci, ale zároveň bude možnost, aby existovala implementace, která je nějakým způsobem nepovoluje, tak mě napadají 3 možné situace:
- vyhodím výjimku typu UnsupportedOperationException
- nevyhodím výjimku, ale indikuji neúspěch návratovou hodnotou
- neudělám ani jedno, prostě jsem potichu a nechám klienta, ať se s tím nějak popere sám

Třetí možnost je očividně evil, ale ani ty první dvě (ač jsou v javoském collections API velmi používané) nejsou žádná výhra.
Koncept "optional" metod sice ve své době měl určité opodstatnění, ale stačí se podívat, kolikrát byla na Stack Overflow řešena [img=http://stackoverflow.com/questions/2965747/why-i-get-unsupportedoperationexception-when-trying-to-remove-from-the-list]http://tahle otázka[/img].

Kdysi jsem na tohle někde četl kritiku, kde byl v nadsázce uvedený takovýhle příklad:
Kód: [Vybrat]

/**
 * Solves all of the word's problems, but may be unsupported.
 *
void solveAllWorldsProblems() {
  throw new UnsupportedOperationException("sorry");
}

Ve chvíli kdy člověk musí přemýšlet nad tím, že nějaká metoda někdy nemusí udělat to, co se od ní očekává, případně může vyrazit výjimku, je prostě další věc, která zvětšuje stavový prostor programu, a tím pádem dělá jeho tvorbu a údržbu složitější a nákladnější.

Když si člověk jednou zkusí, jaké to je, pracovat s neměnnými datovými strukturami a funkcemi bez vedlejších efektů, nemusí při tom přemýšlet nad komplikovanou stavovostí programu a ještě jako bonus nemusí jak opičák všude cpát null checky, tak se pak těžko vrací zpět.  :)