Click here to read this article in English.
Já tem um bom tempo que eu desisti de acompanhar todas as novidades e tendências no mundo das linguagens de programação. Eu ainda lembro quando comecei a programar, usando Clipper! Para obter novidades, ou você comprava um livro ou uma revista…
Hoje em dia quando visito websites como Reddit ou Digg, sempre me deparo com umas 3 APIs AJAX, todas revolucionárias, alguns artigos dizendo que a linguagem XXX é uma droga porque não tem a feature ABC, enquanto outros arigos dizem que ela é a melhor coisa que já aconteceu para os programadores. Nesse mundo dinâmico de hoje, você tem que filtrar aquelas novidades que você quer “digerir”. Se não o fizer a gente acaba conhecendo muito pouco de muita coisa.
Então, há algum tempo comecei a ver artigos sobre Closures em Java. Clicava em alguns, dava uma lida por cima, mas nunca me chamou a atenção. Isso foi antes de eu descobrir os Google Tech Talks, e especificamente a série Advanced Topics on Programming Languages. Neste fim de semana passado acabei assistindo uma palestra de 2 horas sobre Java Closures.
O princípio das Closures não é novo. A proposta é usar variáveis com blocos de código (funções). Eu me lembro que no bom e velho Clipper já haviam os code blocks que eram extremamente úteis. Em javascript também é possível utilizar os Function Objects. E por aí vai…
Tento agora explicar de maneira sucinta o que eu entendi das Closures de Java:
Se você já teve que fazer profiling de código, já se deparou com problemas do tipo:
public void metodoProfile1() {
long t0 = System.currentTimeMillis();
fazAlgumaCoisa();
Logger.log(
"fazAlgumaCoisa(): " +
(System.currentTimeMillis() - t0) +
" ms."
);
}
ou em JDBC:
public List getCustomers() {
List result = new ArrayList();
Connection conn;
Statement s;
ResultSet rs;
try {
conn = ConnectionPool.getConnection();
s = c.createStatement();
rs = s.executeQuery("SELECT * FROM CUSTOMER");
while (rs.next()) {
// ...
}
} catch (SQLException e) {
System.out.println("Error...");
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (c != null) {
try {
c.close();
} catch (SQLException e) {
}
}
}
}
Pegue este último exemplo: você tem 37 linhas de código enquanto a lógica está em menos de 10 linhas. Quando um amigo meu diz que java é uma linguagem burocrática, eu tenho que concordar com ele… Imagine tendo que implementar profiling em 50 métodos do seu projeto e fazer classes de acesso à dados da forma do segundo método? Além de um trabalho extremamente braçal e chato, você acaba com replicação de código. Imagine por exemplo que você queira mudar a forma com que você trata exceções para usar uma API nova de logs… São 50 mudanças e por aí vai…
Aí é que a graça das Closures entram! O que você acha se pudesse escrever algo do tipo:
public void metodoProfile1() {
withMeasure ("metodoProfile1", { =>
fazAlgumaCoisa();
});
}
Ou para o caso de JDBC:
public List getCustomers() {
Connection conn;
Statement s;
ResultSet rs;
with(conn, s, rs, { =>
conn = ConnectionPool.getConnection();
s = c.createStatement();
rs = s.executeQuery("SELECT * FROM CUSTOMER");
if (rs.next()) {
// ...
}
});
}
Esse bloco de código “{ => … }” é uma nova proposta de sintaxe para o Java que reflete variáveis de função. A sintaxe é simples, alguns exemplos:
{int => int} // protótipo de função que recebe um int e retorna um int
{int, int => int} // protótipo de função que recebe dois int's e retorna um int
{String => int throws NumberFormatException} // recebe uma String e retorna int,
// podendo lançar uma NumberFormatException
{ => void } // função sem parâmetros que não retorna nada
{T => U} // função do tipo T que retorna tipo U (generics).
Alguns exemplos de declarações:
{int x => x+1}
{int x, int y => x+y}
{String x => Integer.parseInt(x)}
{=> System.out.println("Hello world");}
{int, int => int} sum = {int x, int y => x+y}; // soma x e y
Toda Closure se transforma “por baixo dos panos” em uma interface que tem um método invoke com a mesma assinatura dos parâmetros da Closure em si. Por exemplo, a variável sum poderia ser executada usando:
(...) System.out.println(sum.invoke(5, 2)); // imprime "7" (...)
… e por aí vai.
O melhor, porém é a sintaxe alternativa, que permite chamadas mais “naturais”. É a chamada “Control Abstract Syntax”, que permite que você escreva o segundo exemplo assim:
public List getCustomers() {
Connection conn;
Statement s;
ResultSet rs;
with (conn, s, rs) {
conn = ConnectionPool.getConnection();
s = c.createStatement();
rs = s.executeQuery("SELECT * FROM CUSTOMER");
if (rs.next()) {
// ...
}
}
}
Lindo, não? Pois é… Veja como a parte de “lógica de negócio” agora está bem mais aparente, você não se “destrai” lendo o método… Se você está pensando como fica o método with aqui, dá uma olhada:
public void with(Connection c, Statement s, ResultSet rs, { => void } block) {
try {
block.invoke();
} catch (SQLException e) {
System.out.println("Error...");
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (s != null) {
try {
s.close();
} catch (SQLException e) {
}
}
if (c != null) {
try {
c.close();
} catch (SQLException e) {
}
}
}
}
Este artigo é apenas uma introdução aos Closures em Java. Existem alguns outros apelos para a criação dessa construção. Para maiores informações eu recomendo a apresentação de Neal Gafter, um dos pais da proposta da closures specification. Esse é o link para o vídeo.
Alguns links relacionados: