Closures i Java
- lör 9 feb, 2008 kl 14:46
- 13 kommentarer
- Closures, Java
Såvida man inte helt ignorerat vad som händer på Java-fronten de seanste två åren så kan man inte ha missat att det förekommer en rätt så hetsig debatt kring något som kallas Closures. Senast så skrev James Gosling ett inlägg på sin blog som genererade kommentarer i en vecka efteråt. Debatten kring detta handlar om huruvida closures ska inkluderas i Java och hur det då ska lösas. Tyvär så är vetskapen om vad en closure är för något ganska begränsad, åtminstone inom Java-världen. Detta beror dels på att begreppet inte riktigt antyder vad det handlar om samt att det inte varit tillgängligt i de main-stream programspråk som vi haft de senaste 20 åren (t.ex. C/C++, Delphi, Java, C# osv.). Nu är dock closures något som är ganska viktigt, både i hur det löses och att utvecklare förstår vad det handlar om, så låt mig göra ett försök att göra bilden lite klarare.
Namnet closures härstammar från programspråket Scheme, som är en Lisp-variant som två killar vid namn Guy Steele och Gerald Jay Sussman skapade i mitten på 70-talet. Problemet med andra Lisp-dialekter var att de var ganska stökiga och komplicerade, så man gjorde en stor uppstädning och resultatet blev ett språk som blev lexically scoped (Hittade ingen bra svensk översättning. Förslag mottages gärna.), till skillnad från tidigare varianter som var dynamically scoped. Om du nu inte har en susning om vad det betyder så innebär det att du är normal och tillhör den stora massan av kodare som haft förmånen att inte behöva bry sig om sådana skumma saker. Jag tänker inte gå in på vad dynamically scoped betyder utan hoppar direkt på att försöka förklara lexically scoped.
I Java har vi scope som innebär att en variabel, metod eller klass har en begränsad ”synlighet”. Det här gör, till exempel, att du kan deklarera en variabel med samma namn både som medlemsvariabel och lokal variabel i en metod. Detta betyder att vår kod har ett visst scope, eller omfång för att använda ett svenskt ord. Det går inte att deklarera två variabler, eller metoder, med samma namn (och parameter-lista om det är en metod) i samma scope. Inga konstigheter här inte, detta är något vi alla kan som ett rinnande vatten. Låt oss gå tillbaka till Scheme och closures.
Scheme införde en möjlighet att deklarera ett lambda uttryck som behåller en referens till alla variabel i det scope som den deklarerades i. Hängde ni med? Ett lambda uttryck är i princip detsamma som en funktion (alltså en metod som inte är kopplad till en viss klass, för er som inte kodat ett funktionellt eller procedurellt språk tidigare). Funktionen kan sen skickas som parameter till andra funktioner (eller i Javas fall, metoder) och kommer behålla alla referenser till de lokala variabler som fanns i det scope som den deklarerades i. De variabler som du sedan använder i funktionen och som är deklarerade i samma scope som funktionen kallas free variables. När sen hela uttrycket, d.v.s. funktionen och dess free variables, evalueras (inte samma sak som att anropa funktionen alltså) så har du en closure.
I Scheme, som är ett rent funktionellt språk (och fantastiskt roligt att koda i, förutsatt att man tycker det är ok att bli konstig i bollen efter några dagar), har ett lexical scope som bara berör variabler. I Java, som är ett mycket mer komplext programspråk, blir det lite mer att tänka på. Först och främst så har Java lite fler saker att hantera i sina scope än vanliga variabler. Du har metoder, typer, checked exception och uttryck såsom break, continue, this och return. Vad skulle det t.ex. innebära om du skrev ”break” i din closures-funktion? Eller ”return”? Tanken är att dessa uttryck ska exekveras i det scope som din closure omslöt. Så om du skapar en closure där du har en return i din funktion så kommer det innebär att du gör en return i den metod som du deklarerade din closure i, och inte return för funktionen. Knepigt? Jo, och det är därför som det är en sån debatt kring det hela. Nu undrar du säkert vad man ska ha det hela till. Om vi klarat oss såhär länge utan closures så kan det väl inte vara så viktigt att få med det nu.
Tyvär är det så att många problem som vi ofta spring på i Java idag blir mycket enklare att lösa med closures. Exempel: alla har vi någon gång kodat ett Swing GUI. När vi ska lägga på en funktion på en knapp så anropar vi något i stil med följande:
addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
// gör nånting...
}
});
Vi skapar alltså en anonym inre klass bara för att lägga lite funktioner på en knapp. När man gjort detta för ett helt GUI så börjar man må smått illa av alla anonyma inre klasser som kladdar ner ens annars så fina kod. Ett annat problem är att om du vill nå en lokal variabel i din actionPerformed-metod så måste den vara final, vilket kanske inte alltid är så lämpligt. Med en closure så blir det dels mindre kod och samtidigt så slipper du deklarera dina variabler med final.
Ovanstående är bara ett av många exempel där closures gör koden mer läsbar (vilket i sin tur innebär färre buggar och enklare att underhålla) och ger oss ett verktyg som kan ersätta många andra tilläggsförslag som gjorts för Java på sistone. Införandet av closures i Java skulle alltså lyfta språket i sin helhet och minska behovet att bygga in ytterligare komplicerade konstruktioner. Det är alltså inte så att closures ger dig en möjlighet att göra saker du inte kunde göra tidigare. Java är ju ett Turing-komplett språk, så allt går faktiskt att uttrycka redan idag. Problemet är alltså att många återkommande uttryck är på tok för komplexa och större än vad de borde vara.
Så hur der det ut med Closures i Java då? Som det verkar just nu så kommer vi se att det kommer med i Java 7. Neal Gafter är en av de som jobbar mest med detta och skriver rätt mycket om det på sin blogg (när han inte skriver om Java Puzzlers och annat skoj) . Bob Lee (som också jobbar på Google) har presenterat en alternativ, light-variant, av closures för Java (syntaktisk socker ovanpå anonyma inre klasser i princip). Sen finns det en hel webb-plats som är dedikerad enbart för Java Closures och hur det kan lösas. Det finns en hel del att läsa om det hela och du kan även tanka hem olika implementationer av det hela för att testa.
Det är starkt rekommenderat att man sätter sig in i och lär sig förstå vad closures är för något, eftersom det garanterat kommer komma med i nästa version av Java och detta är en ändring av språket som är mycket större än vad auto-boxing, generics och annotations var när Java 5 kom. Jag kan rekommendera två presentationer som gjordes förra året och som finns upplagda på Parleys.com. Den ena var vad som genererade Goslings sensate blogginlägg i ämnet och den andra är Neal Gafters uppdatering vad som hänt på closures-fronten rent tekniskt.
Closures finns annars i JavaScript, men det vet kanske inte så många.
Mats: Det är sant att closures finns i JavaScript samt de flesta andra script-språk som vi använder idag (Ruby, Groovy etc.). Dock är det inte helt vanligt att man lär ut dessa språk (eller i varje fall closures) på högskolan, vilket alltså innebär att de flesta som läser data- eller systemvetenskap inte har en susning om vad det är.
Closures, First class properties, multiline strings och sen får det va nog ;-) Det hade varit bra om det kunde bli en riktigt stabil baseline i Javaspråket. Skall man konkurrera med C och C++ på Linux så måste man nog försöka få fram en stabil plattform som inte ändras allt för mycket så att man kan skapa lite långsiktighet. Jag gillar verkligen förändring men eftersom alla inte gör det och kanske inte uppdaterar sina JRE’s så ofta som man kunde önska så hade det varit bra om det blev stabilare.
* Closures, closures and more closures
o http://blogs.sun.com/jag/entry/closures
o http://gafter.blogspot.com/2008/02/closures-puzzler-neapolitan-ice-cream.html
o http://weblogs.java.net/blog/kirillcool/archive/2008/02/and_so_it_begin.html
o http://slm888.com/javac/
o http://docs.google.com/View?docid=k73_1ggr36h&
o http://www.jroller.com/scolebourne/
Saxat från Java Posse #163 – Newscast (http://javaposse.com/index.php?post_id=305735#)
Closures är ett kraftfullt verktyg att ha i lådan, men det är inte svårt alls att skriva totalt oläslig kod. Just det faktum att scopet är detsamma som när closuren deklarerades kräver att man håller tungan i rätt mun. För att minska risken för misstag och svårläst kod kan det vara lämpligt att försöka hålla dem till ett absolut minimum vad gäller antalet rader kod. Detta gäller nog i fler fall än vad det gör för vanliga metoder. Bortsett från detta så är closures en fröjd att jobba med och som Erik säger så löser de många vardagliga problem på ett elegant sätt:
['This', 'is', 'a', 'closure'].sortBy(function(s) {
return s.length;
});
Closures, First class properties, multiline strings OCH ASPEKTER och SEN får det va nog ;-)
First class properties: Sitter du och skriver Java i Notepad eller? Det finns något som heter CODE COMPLETION. ;) Men seriöst talat så är detta en no-issue. Även om getters och setters inte är helt objektorienterat så är det en smart lösning. Din property är både en typ (klassen som returneras) och en metod. ÄR ju en strålande lösning ändå.
Multiline Strings: Detta är en funktion på strängar och inte en typ. Ska richtext också läggas till då eller?
Aspekter: En tillämpning av annotations. Detta är väl ändå löst idag? Pointcuts suger och är write-once.
Det handlar bara om läsbarhet.. Allt som gör koden mer läsbar röstar jag för.
Properties:
obj1.setVar(obj2.getVar())v.s
obj1.var = obj2.varMultiline Strings:
String head = String.format(
"<html>" +
" <head>" +
" <title>%s</title>" +
" </head>" +
"<body>"
, title);
v.s
String head = String.format(
""
<html>
<head>
<title>%s</title>
</head>
<body>
""
, title);
@Per Arneng:
Tycker du att Java måste bli mer stabilt för att konkurrera med C och
C++? För att ”skapa lite långsiktighet”? Förstår inte vad du menar.
Nya versioner av Java kommer ju bara ungefär vartannat år, och de är
bakåtkompatibla varje gång.
Mats Henricson:
Eftersom det kommer nya features rätt ofta (ofta är ju relativt men om man jämför till C eller C++) och företagen tar in dessa features så måste man ju hela tiden uppgradera JRE’n på sin lokal burk för att kunna köra det senaste programmet från ett företag.
Men om företagen har en fast klient miljö som inte tillåter annan JRE än tex 1.4 så blir det ju svårt att köra program som är skrivna för 1.5 och 1.6 om man inte använder sig av specialverktyg. Så om man stoppar in alla features som man behöver nu och sen är nöjd så blir det enklare att hantera bakåt kompatibiliteten.
Inget av det som kommer in i Java är ju direkt nya idéer så dom borde väl bara kunna stoppa in det som dom vill i en stor release istället för att pyttsa ut det på små releaser som bryter bakåt kompatibiliteten.
Tydligen heter det inte bakåt kompatibillitet utan framåt kompatbillitet och man bryter den mellan 1.4 -> 1.6 och förmodligen i 1.7:an också om man skall ha in tex closures.
Här står mer om generics och 1.4:
http://forum.java.sun.com/thread.jspa?threadID=321534&forumID=316
Jag hoppas verkligen closures i Java blir kommer funka på ett bra sätt. Har man en gång haft möjlighet att arbeta med closures (eller syntaxtiskt smidiga metodobject) så saknar man dem då man inte längre har dem.
För den intresserade så har jag lekt hur man skulle kunna få något som liknar dynamisk scoping i Java: http://programaticallyspeaking.blogspot.com/2008/04/dynamic-scoping-as-alternative-to.html
Bra artikel – några kommentarer dock:
1. javac.info sajten handlar endast om Gafters BGGA version.
2. Det finns säkert ett dussintal closures proposals för Java vid det här laget, men dom alternativen som diskuteras mest tror jag är
-BGGA
-CICE
-FCM+JCA
-”No closures!”
3. Det är långt ifrån säkert att closures kommer med i Java7… men den heta closures debatten är nog en bidragande orsak att Sun fortsätter att vara tysta om när Java 7 kan väntas komma. Det har väl inte ens annonserats en JSR för Java 7 än.
Den underläggande problematiken tror jag är – vem bestämmer språkets och platformens framtid nu när vi har både JCP, Sun och open source processen? Closures har bara drivit detta till sin spets.
”Dock är det inte helt vanligt att man lär ut dessa språk (eller i varje fall closures) på högskolan, vilket alltså innebär att de flesta som läser data- eller systemvetenskap inte har en susning om vad det är.”
Läser man en riktig utbildning, såsom datateknik på Chalmers/LTH/KTH eller liknande, så ”tvingas” man läsa kurser i något funtionellt språk som Haskell eller Lisp och har därmed 100%:ig koll på closures, list comprehension osv