Existe a possibilidade de que na próxima versão de Java a linguagem ganhe suporte a closures (mais aqui, e aqui). Pelo que andei vendo acho que dá para simular algo parecido com closures em C# também usando delegate. O mais legal de tudo isso é ver o que as pessoas acham que são closures e como usam.

Um exemplo disso é o texto de Martin Fowler, que usa Ruby como linguagem para explicar o que é closure.

Tá então closure é uma função que eu posso “salvar” umas variáveis e retornar outra função. Que ótimo! Agora não vou for ter que ficar fazendo inner class e me preocupar com tudo seja final quando for implementar meu callback! E na boa, é só pra isso que closure vai ser usado em Java se um dia entrar na linguagem.

Mas closure vai muito além disso e infelizmente pouca gente vai continuar usando para construir abstrações. Porque o que falta em textos como o do Martin Fowler é dizer como usar isso para construir uma propriedade de closure, ou seja, uma operação ou conjunto de operações fechadas sobre si mesmas, adicionando assim um nivel de abstração.

O exemplo clássico de closure usada dessa maneira aparece no começo do SICP. Livro altamente recomendado. Se possível veja os videos também. Tudo disponível online.

O exemplo é mais ou menos assim. Lisp quem três primitivas que manipulam pares:

  • cons: une 2 elementos em um par (um cons)
  • car: retorna o primeiro elemento do par
  • cdr: retorna o segundo elemento do par

Usando Ruby, vamos dizer que temos essas três operações. Um cons pode unir dois elementos quaisquer, independente de tipo. Assim:

car(cons(1, 4.2)) # retorna 1
cdr(cons(1, 4.2)) # retorna 4.2

Com isso podemos construir listas, como feito em Lisp, porque podemos fazer cons de cons. Uma maneira (convensão) de se fazer isso é assim:

# representa [1, 2, 3, 4]
lst = cons(1, cons(2, cons(3, cons(4, nil))))

Com essa representação de lista podemos construir funções como por exemplo mapcar (que é bem parecida com map de Ruby):

def mapcar(proc, lst)
  if lst == nil
    nil
  else
    cons(proc.call(car(lst)), mapcar(proc, cdr(lst)))
  end
end

Com mapcar podemos fazer inúmeras coisas. Um exemplo bobo é a função scale que recebe um multiplicador e uma lista e retorna outra lista com todos os elementos multiplicados por esse fator:

def scale(factor, lst)
  mapcar(Proc.new {|e| e * factor}, lst)
end

Da mesma forma que escrevemos mapcar podemos escrever outras funções genéricas sobre listas como reduce e filter (em Lisp o efeito de filter é obtido com remove-if).

def reduce(proc, init, lst)
  if lst == nil
    init
  else
    proc.call(car(lst), reduce(proc, init, cdr(lst)))
  end
end

def filter(proc, lst)
  if lst == nil
    nil
  else
    if proc.call(car(lst))
      cons(car(lst), filter(proc, cdr(lst)))
    else
      filter(proc, cdr(lst))
    end
  end
end

Tá, e o que tudo isso tem a ver com closures? Bom, essas funções e a infinidade de abstrações que podem ser construidas a partir delas utilizaram apenas um conjunto fechado de procedimento sobre listas: cons, car e cdr. Sendo que cons é completamente genérica, permitindo que se faça cons de cons. Assim podemos escrever listas, árvores e estruturas cada vez mais complexas tendo como base essas funções simples.

E como cons, car e cdr podem ser implementados? Closures, claro:

def cons(a, b)
  Proc.new { |first|
    if first == true
      a
    else
      b
    end
  }
end

def car(c)
  c.call(true)
end

def cdr(c)
  c.call(false)
end