to imho neni ani ted zapotrebi:
interface Iface { public int exec(int x); } public class Test { int x = 10; public void run() { int y = 20; Iface ii = new Iface() { public int exec(int x) { return x+1; } }; System.out.println(ii.exec(1)); System.out.println(ii.exec(2)); } public static void main(String[] args) { new Test().run(); } }
tj. normalne se pristupuje jak k promennym tak i k atributum.
Tradiční řešení přístupu k lokálním proměnným je zabalit je do pole o velikosti jedna.
final int[] y = new int[1]; y[0] = 0; new Runnable() { public void run() { y[0]++; } }.run(); assert y[0] == 1;
Neexistuje jediný technický důvod, proč by překladač Javy nemohl automaticky balit proměnné ze zásobníku. Lidé by pak klidně mohli psát:
int y = 0; new Runnable() { public void run() { y++; } }.run(); assert y == 1;
což by bylo přirozené a čitelnější. Překladač by to pak upravil do výše popsané formy pole s jedním prvkem. Všichni vědí, že by to šlo a čas od času někdo i navrhne, že by se to mohlo udělat. Bohužel tento nápad má v Santa Kláře vlivné odpůrce, kteří jej vždy smetou ze stolu.
Mám pocit, že jeden z návrhů na lambdy obsahoval i možnost plnohodnotného přístupu na lokální proměnné (snad se musely oanotovat pomocí @Shared), ale nevsadil bych si na to, že projde.
Myslím, že důvod je ten, že při překladu té anonymní třídy se z ní udělá obyčejná vnitřní třída a hodnota proměnné y
se do ní promítne jako konstanta (atribut s modifikátorem final
). Nic jiného než konstanta to ani být nemůže, protože proměnná y
vnější třídy je mimo dosah (scope) – kód definovaný ve vnitřní třídě by za běhu neměl jak zjistit její hodnotu. Z toho pramení požadavek na neměnnost proměnné y
.
K atributům vnější třídy má AnonClass
přístup, takže tam požadavek na neměnnost hodnoty není.
Tak trochu se musim opravit, nemoznost pouziti
non-final varible prameni z toho ze local (non-final)
variable neprezijou navrat z funkce.
K y neni pristup ve vnitrni tride protoze y "zije"
jenom na stacku funkce "run" takze po navratu z funkce
"run" y neexistuje.
S tim scopem bych byl opatrnejsi, protoze final variable
neni jenom "compile-time" konstanta jako treba
final String test="test".
Muze to byt i final String test=new String("test")
coz uz je resolvovany v runtime a presto stale pouzitelny
ve vnitrni tride.
Když jsem psal o konstantě, měl jsem na mysli field (atribut) s modifikátorem final
, bez ohledu na to, zda je jeho hodnota vyhodnocená při kompilaci nebo až za běhu. (Tuším, že u anonymních vnořených tříd je vždy předávaná prostřednictvím konstruktoru.)
Souhlasím, že vaše vysvětlení, že ne- final
proměnná „nepřežije“ návrat z funkce, protože „žije“ jen na zásobníku (stacku), je přesnější než vysvětlení se scopem.
Taky jsem to sem chtěl ihned napsat. Ke Scale mám takovou teorii jak vznikla:
Nějaký, řekněme, Pythonista pořád předhazoval Marku Oderskemu lehkost dynamicky typovaných jazyků. On jako správný zastánce statického typování si nemohl odpustit poznámku "To by stejně dobře mohl umět staticky typovaný jazyk.". "Tak mi nějaký ukaž!", řekl Pythonista. Tak vznikla sázka. A ze sázky Scala.
Je jedno, jestli tomu tak skutečně bylo, ale Scala tak prostě vypadá.
(Mimochodem, Smalltalk prý vznikl sázkou, tak proč by Scala nemohla?)
kdyz se na ten zapis divam, nebylo by lepsi to udelat stejne jako napriklad v Pythonu nebo i v Lispu :-), tj. pres klicove slovo lambda? Takhle se to zacne ve zdrojacich hemzit krizkama a hlavne zavorkama {}, ktere vsak budou mit zase o dalsi vyznam vic - zatim oznacuji predevsim lexikalni bloky, ale pouzivaji se napriklad i pri inicializaci poli, anonymnich trid (ale jinak nez s #) atd. takze se jedna o pomerne dost "pretizeny" znak ;)
stejne se driv nebo pozdeji bude do Javy pridavat nejaka konstrukce, ktere klicove slovo bude potrebovat, v minulosti si vzpominam na "enum", jehoz zavedeni rozbilo nejake programy - neco z Apache.org to bylo.
S tou lambdou to neni spatnej napad, stejne se # nikde jinde (krome znakovych konstant a retezcovych literalu) nepouziva, takze by se to asi do IDE dalo dobastlit. No v kazdem pripade uvidime, jak se treba Eclipse popere s JDK 7, treba Netbeans uz nejakou podporu maji (na druhou stranu se vsak Eclipse vyviji rychleji).
Podle me pro novy klicovy slova existuje trivialni a bezproblemovy reseni: dopsat do zdrojaku verzi, treba jako
package 8 cz.root;
// zdrojak je java8, lambda je klicovy slovo
Prekladac by vedel s jakou verzi dela a vsechno by bylo jasny. Starsi prekladac by tomu nerozumel ale novy syntaxi taky ne, takze to nevadi.
Neslo by to pouzit pro root-package, ale ta je stejne celkem k nicemu.
Je to mozna trosku divny, ale skutecne tam enum nebylo, je to popsany napriklad ve starsich JLS (Java Languange Specification):
http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#229308
Mam takovou teorii, ze v honbe za co nejvetsim "odtrhnutim se" od svych C/C++ korenu tvurci Javy zpocatku vsechno vsadili na rozhrani a tridy, pricemz na vyctovy typ (velmi dulezity) zapomeli, takze nas nakonec musel Josh Bloch ucit, jak spravne vycet nahradit tridou ;-)
Zdá se, že v současnosti je tedy jedna lambda implementována jako jedna třída. Mám strach, že to dost zatíží permgen a doufám, že je to jen dočasné a nakonec se přejde na efektivnější variantu.
Ten dolar mě trošku překvapil - on bývá běžně vnitřně používán na vnitřní a anonymní třídy. To znamená, že pokud odstraním příznak synthetic, tak mohu používat tyto třídy i pomocí dolaru? Třeba takto?:
Foo.java:
class Foo{ private static Object x = new Comparator<String>(){...} }
Bar.java:
public class Bar{ public static void main(String...args){ System.out.println((new Foo$1().compare("a", "b")); } }
je to presne jak pisete. Podivejte se prosim na nasledujici zdrojak, samozrejme prelozitelny.
(chybi typ u seznamu, ma tam byt samozrejme String, ale kvuli orezani HTML znacek jsem to radeji odtranil, tak me nebijte :-)
import java.util.*; class Test$1 { } class Test$2 { } public class Test { public static void main(String[] args) { List list = new ArrayList(){ { add("hello"); add("world"); } }; } }
Vysledkem prekladu budou ctyri soubory:
jinymi slovy - prekladac si nejakou magii zjisti, jak anonymni tridy pojmenovat a prida do bajtkodu priznak synthetic.
Ahoj, pěkný článek! Jenom poznámka k použití Runnable v příkladu - mělo by odpovídat korektnímu použití, aby byl příklad správný.
Tedy místo
Runnable r = #{ System.out.println("Blah") };
r.run();
by mělo být
Runnable r = #{ System.out.println("Blah") };
new Thread(r).start();
Co do toho motáte SCJP? Na rozhraní Runnable není naprosto nic zvláštního, a pokud se autor nesnažil spustit vlákno, proč by prostě nemohl zavolat jeho metodu? Runnable je prostě objekt reprezentující kód, a jestli chci nebo nechci spustit jeho kód v samostatném vlákně přeci ani nemusím v okamžiku psaní kódu vědět.