Vamos começar com um exemplo concreto: você deve conhecer a interface Closeable em Java. Ele permite que você gerencie recursos que podem ser fechados de forma concisa. O problema com essa solução é que ela só funcionará com tipos que já a implementam. E a lista é bem longa, mas não inclui aquela nova classe brilhante que você criou ontema classe daquela brilhante biblioteca de código aberto que você usa; e, infelizmente, você não pode modificar essa classe.
Gostaríamos de poder usar a digitação de pato : se houver um método de fechamento, ele pode ser fechado. No Scala, você pode conseguir isso com tipagem estrutural:
def withResource[T](r: {def close(): Unit}, f: => T): T = {
try {
f
} finally {
// let me simplify things here by assuming close is not throwing exceptions itself
r.close()
}
}
Aqui, o tipo de r pode ser qualquer tipo que tenha um método de fechamento que não receba parâmetros e retorne unidade. O interessante sobre isso é que você pode usar a função withResource com tipos para os quais não a projetou, desde que tenham a estrutura que você definiu. Daí o nome.
O problema com esta solução é que ela tem implicações de tempo de execução, uma vez que Scala usará introspecção em tempo de execução.
Mas nós podemos fazer melhor. Digite classes de tipo:
trait Closeable[R] {
def close(r: R): Unit
}
object Closeable {
implicit object SqlConnectionIsCloseable extends Closeable[Connection] {
def close(conn: Connection) = conn.close()
}
}
def withResource[R:Closeable, T](r: R, f: => T): T = {
try {
f
} finally {
implicitly[Closeable[R]].close(r)
}
}
Closeable é uma classe de tipo: define uma classe de tipos que inclui todos os tipos que podem ser fechados, assim como o tipo estrutural que usamos antes. Mas, então, você adiciona um tipo à classe definindo um objeto implícito que diz ao compilador como esse tipo pertence à classe de tipo e como o método close é implementado para essa classe específica.
Quando você define um método que leva um parâmetro que “pode ser fechado” (r no exemplo), você simplesmente usa um limite de contexto informando ao compilador que deve haver um objeto implícito cujo tipo é Closeable [R], onde R é o tipo de o parâmetro. Então, implicitamente [Closeable [R]] é um truque para obter essa instância.
Agora você pode estender a solução apenas criando novos objetos implícitos para novos tipos que você descobrir que podem ser fechados de alguma forma, mesmo se o método não for denominado close.