|
| 1 | +--- |
| 2 | +layout: tutorial |
| 3 | +title: Auto Referências Explicitamente Tipadas |
| 4 | + |
| 5 | +disqus: true |
| 6 | + |
| 7 | +tutorial: scala-tour |
| 8 | +num: 24 |
| 9 | +tutorial-next: implicit-parameters |
| 10 | +tutorial-previous: compound-types |
| 11 | +language: pt-br |
| 12 | +--- |
| 13 | + |
| 14 | +Ao desenvolver um software extensível, às vezes é útil declarar explicitamente o tipo do valor `this`. Para ilustrar isso, criaremos uma pequena representação extensível de uma estrutura de dados de grafo em Scala. |
| 15 | + |
| 16 | +Aqui está uma definição que descreve um grafo: |
| 17 | + |
| 18 | +```tut |
| 19 | +abstract class Graph { |
| 20 | + type Edge |
| 21 | + type Node <: NodeIntf |
| 22 | + abstract class NodeIntf { |
| 23 | + def connectWith(node: Node): Edge |
| 24 | + } |
| 25 | + def nodes: List[Node] |
| 26 | + def edges: List[Edge] |
| 27 | + def addNode: Node |
| 28 | +} |
| 29 | +``` |
| 30 | + |
| 31 | +Um grafo consiste em uma lista de nós e arestas onde o nó e o tipo de aresta são declarados como abstratos. O uso de [tipos abstratos](abstract-types.html) permite que a implementação da trait `Graph` forneça suas próprias classes concretas para nós e arestas. Além disso, existe um método `addNode` para adicionar novos nós a um grafo. Os nós são conectados usando o método `connectWith`. |
| 32 | + |
| 33 | +Uma possível implementação de `Graph` é ilustrada na classe a seguir: |
| 34 | + |
| 35 | +```tut:fail |
| 36 | +abstract class DirectedGraph extends Graph { |
| 37 | + type Edge <: EdgeImpl |
| 38 | + class EdgeImpl(origin: Node, dest: Node) { |
| 39 | + def from = origin |
| 40 | + def to = dest |
| 41 | + } |
| 42 | + class NodeImpl extends NodeIntf { |
| 43 | + def connectWith(node: Node): Edge = { |
| 44 | + val edge = newEdge(this, node) |
| 45 | + edges = edge :: edges |
| 46 | + edge |
| 47 | + } |
| 48 | + } |
| 49 | + protected def newNode: Node |
| 50 | + protected def newEdge(from: Node, to: Node): Edge |
| 51 | + var nodes: List[Node] = Nil |
| 52 | + var edges: List[Edge] = Nil |
| 53 | + def addNode: Node = { |
| 54 | + val node = newNode |
| 55 | + nodes = node :: nodes |
| 56 | + node |
| 57 | + } |
| 58 | +} |
| 59 | +``` |
| 60 | + |
| 61 | +A classe `DirectedGraph` estende a classe `Graph` fornecendo uma implementação parcial. A implementação é apenas parcial porque gostaríamos de poder ampliar o `DirectedGraph`. Portanto, esta classe deixa todos os detalhes de implementação abertos e assim, tanto as arestas e os nós são definidos como abstrato. No entanto, a classe `DirectedGraph` revela alguns detalhes adicionais sobre a implementação do tipo das arestas ao restringir o limite de tipo para a classe `EdgeImpl`. Além disso, temos algumas implementações preliminares de arestas e nós representados pelas classes `EdgeImpl` e `NodeImpl`. Uma vez que é necessário criar novos objetos nó e aresta dentro da nossa implementação de grafo, também temos que adicionar os métodos de construção `newNode` e `newEdge`. Os métodos `addNode` e `connectWith` são ambos definidos em termos destes métodos de construção. Uma análise mais detalhada da implementação do método `connectWith` revela que, para criar uma aresta, temos que passar a auto-referência `this` para o método de construção `newEdge`. Mas a `this` é atribuído o tipo `NodeImpl`, por isso não é compatível com o tipo `Node` que é exigido pelo método de construção correspondente. Como consequência, o programa acima não é bem-formado e o compilador Scala irá emitir uma mensagem de erro. |
| 62 | + |
| 63 | +Em Scala é possível vincular uma classe a outro tipo (que será implementado no futuro) ao fornecer a auto referência `this` ao outro tipo explicitamente. Podemos usar esse mecanismo para corrigir nosso código acima. O tipo explícito de `this` é especificado dentro do corpo da classe `DirectedGraph`. |
| 64 | + |
| 65 | +Aqui está o programa já corrigido: |
| 66 | + |
| 67 | +```tut |
| 68 | +abstract class DirectedGraph extends Graph { |
| 69 | + type Edge <: EdgeImpl |
| 70 | + class EdgeImpl(origin: Node, dest: Node) { |
| 71 | + def from = origin |
| 72 | + def to = dest |
| 73 | + } |
| 74 | + class NodeImpl extends NodeIntf { |
| 75 | + self: Node => // nova linha adicionada |
| 76 | + def connectWith(node: Node): Edge = { |
| 77 | + val edge = newEdge(this, node) // agora válido |
| 78 | + edges = edge :: edges |
| 79 | + edge |
| 80 | + } |
| 81 | + } |
| 82 | + protected def newNode: Node |
| 83 | + protected def newEdge(from: Node, to: Node): Edge |
| 84 | + var nodes: List[Node] = Nil |
| 85 | + var edges: List[Edge] = Nil |
| 86 | + def addNode: Node = { |
| 87 | + val node = newNode |
| 88 | + nodes = node :: nodes |
| 89 | + node |
| 90 | + } |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +Nesta nova definição de classe `NodeImpl`, `this` tem o tipo `Node`. Como o tipo `Node` é abstrato e, portanto, ainda não sabemos se `NodeImpl` é realmente um subtipo de `Node`, o sistema de tipo Scala não nos permitirá instanciar esta classe. Mas, no entanto, declaramos com a anotação de tipo explícito que, em algum ponto, (uma subclasse de) `NodeImpl` precisa denotar um subtipo de tipo `Node` para ser instantiável. |
| 95 | + |
| 96 | +Aqui está uma especialização concreta de `DirectedGraph` onde todos os membros da classe abstrata são definidos: |
| 97 | + |
| 98 | +```tut |
| 99 | +class ConcreteDirectedGraph extends DirectedGraph { |
| 100 | + type Edge = EdgeImpl |
| 101 | + type Node = NodeImpl |
| 102 | + protected def newNode: Node = new NodeImpl |
| 103 | + protected def newEdge(f: Node, t: Node): Edge = |
| 104 | + new EdgeImpl(f, t) |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +Observe que nesta classe, podemos instanciar `NodeImpl` porque agora sabemos que `NodeImpl` representa um subtipo de tipo `Node` (que é simplesmente um alias para `NodeImpl`). |
| 109 | + |
| 110 | +Aqui está um exemplo de uso da classe `ConcreteDirectedGraph`: |
| 111 | + |
| 112 | +```tut |
| 113 | +object GraphTest extends App { |
| 114 | + val g: Graph = new ConcreteDirectedGraph |
| 115 | + val n1 = g.addNode |
| 116 | + val n2 = g.addNode |
| 117 | + val n3 = g.addNode |
| 118 | + n1.connectWith(n2) |
| 119 | + n2.connectWith(n3) |
| 120 | + n1.connectWith(n3) |
| 121 | +} |
| 122 | +``` |
0 commit comments