Skip to content

Commit 0928735

Browse files
committed
Tutorials - Translation PT-BR - Explicitly Typed Self Refs
1 parent 8b647a8 commit 0928735

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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

Comments
 (0)