Em Scala é possível declarar classes que tenham outras classes como membros. Em contraste com a linguagenm Java, onde classes internas são membros da classe em que foram declaradas, em Scala as classes internas são ligadas ao objeto exterior. Para ilustrar essa diferença, rapidamente esboçamos a implementação de grafo como um tipo de dados:
class Graph {
class Node {
var connectedNodes: List[Node] = Nil
def connectTo(node: Node): Unit = {
if (!connectedNodes.exists(node.equals)) {
connectedNodes = node :: connectedNodes
}
}
}
var nodes: List[Node] = Nil
def newNode: Node = {
val res = new Node
nodes = res :: nodes
res
}
}
Em nosso programa, os grafos são representados por uma lista de nós. Os nós são objetos da classe interna Node
. Cada nó tem uma lista de vizinhos, que são armazenados na lista connectedNodes
. Agora podemos configurar um grafo com alguns nós e conectar os nós de forma incremental:
def graphTest: Unit = {
val g = new Graph
val n1 = g.newNode
val n2 = g.newNode
val n3 = g.newNode
n1.connectTo(n2)
n3.connectTo(n1)
}
Agora melhoramos o exemplo acima com tipos, para assim declarar explicitamente qual o tipo das várias entidades definidas:
def graphTest: Unit = {
val g: Graph = new Graph
val n1: g.Node = g.newNode
val n2: g.Node = g.newNode
val n3: g.Node = g.newNode
n1.connectTo(n2)
n3.connectTo(n1)
}
Este código mostra claramente que o tipo nó é prefixado com sua instância externa (em nosso exemplo é o objeto g
). Se agora temos dois grafos, o sistema de tipos de Scala não nos permite misturar nós definidos dentro de um grafo com os nós de outro, já que os nós do outro grafo têm um tipo diferente.
Aqui está um programa inválido:
object IllegalGraphTest extends App {
val g: Graph = new Graph
val n1: g.Node = g.newNode
val n2: g.Node = g.newNode
n1.connectTo(n2) // legal
val h: Graph = new Graph
val n3: h.Node = h.newNode
n1.connectTo(n3) // illegal!
}
Observe que em Java a última linha no programa do exemplo anterior é válida. Para nós de ambos os grafos, Java atribuiria o mesmo tipo Graph.Node
; isto é, Node
é prefixado com a classe Graph
. Em Scala, esse tipo também pode ser expresso, e é escrito Graph#Node
. Se quisermos ser capazes de conectar nós de diferentes grafos, temos que mudar a definição inicial da nossa implementação do grafo da seguinte maneira:
class Graph {
class Node {
var connectedNodes: List[Graph#Node] = Nil
def connectTo(node: Graph#Node): Unit = {
if (!connectedNodes.exists(node.equals)) {
connectedNodes = node :: connectedNodes
}
}
}
var nodes: List[Node] = Nil
def newNode: Node = {
val res = new Node
nodes = res :: nodes
res
}
}
Note que este programa não nos permite anexar um nó a dois grafos diferentes. Se quisermos também remover esta restrição, temos de mudar o tipo da variável
nodes
paraGraph#Node
.
Contributors to this page:
Contents
- Introdução
- Basics
- Tipos Unificados
- Classes
- Parâmetro com Valor Padrão
- Parâmetros Nomeados
- Traits
- Tuplas
- Composição de Classes Mixin
- Funções de ordem superior
- Funções Aninhadas
- Currying
- Classes Case
- Correspondência de Padrões
- Objetos Singleton
- Padrões de Expressões Regulares
- Objetos Extratores
- For Comprehensions
- Classes Genéricas
- Variâncias
- Limitante Superior de Tipos
- Limitante Inferior de Tipos
- Classes Internas
- Tipos Abstratos
- Tipos Compostos
- Auto Referências Explicitamente Tipadas
- Parâmetros Implícitos
- Conversões Implícitas
- Métodos Polimórficos
- Inferência de Tipo Local
- Operadores
- By-name Parameters
- Anotações
- Packages and Imports
- Package Objects