ScalaTutorial-pt BR PDF

Você também pode gostar

Fazer download em pdf ou txt
Fazer download em pdf ou txt
Você está na página 1de 15

Um tutorial de Scala

para programadores Java

Verso 1.2
18 de setembro de 2008

Michel Schinz
Philipp Haller
Traduo:
Marcelo Castellani
Thiago Rocha

P ROGRAMMING M ETHODS L ABORATORY


EPFL
S WITZERLAND

Introduo

Este documento visa dar uma rpida introduo a linguagem Scala e seu compilador. Ele dirigido a pessoas que realmente possuem alguma experincia em programao e desejam uma viso geral da linguagem Scala. Assumimos que voc possui conhecimento bsico em conceitos de orientao a objeto, especialmente em
Java.

Um primeiro exemplo

Como primeiro exemplo usaremos o clssico Hello world. Ele no um programa


fascinante, mas com ele fica simples de demonstrar o uso de ferramentas Scala sem
falar muito da linguagem. Ele ficar como abaixo:
object HelloWorld {
def main(args: Array[String]) {
println("Hello, world!")
}
}

A estrutura deste programa deve ser familiar para programadores Java: ela consiste
de um mtodo chamado main que recebe os argumentos da linha de comando, um
array de string, como parmetro; o corpo deste mtodo consiste de uma chamada
nica ao mtodo pr-definido println que recebe nossa amigvel saudao como
argumento. O cdigo de main no retorna um valor, dessa forma no necessrio
declarar um valor de retorno.
O que no to familiar assim para programadores Java a declarao object que
contm o mtodo main. Essa declarao introduz o que comumente chamado
de objeto singleton, que uma classe que possuir apenas uma nica instncia. A
declarao acima contri tanto a classe chamada HelloWorld quanto sua intncia,
tambm chamada de HelloWorld. Esta instncia criada sob demanda, no momento do seu primeiro uso.
O leitor mais astuto pode ter percebido que o cdigo do mtodo main no declarado
como static aqui. Isso ocorre por que membros estticos (mtodos ou campos)
no existem em Scala. Ao invs de usar mtodos estticos, o programador Scala
declara esses membros como objetos singleton.

2.1 Compilando o exemplo


Para compilar nosso cdigo voc deve usar scalac, o compilador Scala. scalac funciona como a maioria dos compiladores: ele recebe um fonte como argumento e
talvez algumas opes, e produz um ou mais arquivos objeto. Os arquivos objetos
aqui produzidos so arquivos .class padro Java.

2.2

Rodando o exemplo

Se voc salvar o cdigo acima num arquivo chamado HelloWorld.scala, voc pode
o compilar usando o comando abaixo (o sinal de maior > representa o prompt de
comando e no deve ser digitado):
> scalac HelloWorld.scala

Isso ir gerar uma srie de classes no diretrio corrente. Uma dessas classes ser
chamada de HelloWorld.class, e contm a classe que deve ser diretamente executada pelo comando scala, como ser mostrado a seguir.

2.2 Rodando o exemplo


Uma vez compilado, um programa Scala pode ser facilmente executado atravs do
comando scala. Seu uso bem parecido com o do comando java, que usado
para executar programas Java, e aceita as mesmas opes. O exemplo acima pode
ser executado com o comando a seguir, que produz a sada esperada:
> scala -classpath . HelloWorld
Hello, world!

Integrando com o Java

Um dos maiores poderes da Scala sua capacidade de integrao fcil com o Java.
Todas as classes do pacote java.lang so importadas por padro, enquanto que
todas as outras podem ser importadas explicitamente.
Vamos ver um exemplo que demonstra isso. Ns precisamos obter e formatar a
data corrente de acordo com a conveno usada num pas especfico, que no nosso
exemplo ser a Frana 1 .
A biblioteca de classes Java define um poderoso conjunto de classes utilitrias, como
Date e DateFormat. Como Scala interage diretamente com Java, no existem classes
equivalentes na biblioteca de classes Scala: ns simplesmente importamos os pacotes Java correspondentes:
import java.util.{Date, Locale}
import java.text.DateFormat
import java.text.DateFormat._
object FrenchDate {
def main(args: Array[String]) {
val now = new Date
val df = getDateInstance(LONG, Locale.FRANCE)
1

Outras regies como os falantes de francs em parte da Suia usam a mesma conveno

println(df format now)


}
}

O import da Scala bem parecido com o seu equivalente em Java, mas ainda mais
poderoso. Multiplas classes podem ser importadas de um mesmo pacote atravs do
uso de chaves, como na primeira linha. Outra diferena que quando desejamos
importar todas as classes de um pacote usamos o sublinhado (underscore) (_) ao
invs do asterisco (*). Isso ocorre por que o asterisco um identificador vlido em
Scala (por exemplo, o nome de um mtodo), como veremos mais a frente.
O import na terceira linha traz todos os membros da classe DateFormat. Isso faz o
mtodo esttico getDateInstance e o campo esttico LONG diretamente visiveis.
Dentro do mtodo main ns primeiro criamos uma instncia da classe Date do Java,
que por padro contm a data atual. A seguir, ns definimos o formato da data
usando o mtodo esttico getDateInstance, que importamos anteriormente. Finalmente, ns imprimimos a data atual formatada de acordo com a instncia localizada de DateFormat. Esta ltima linha mostra uma propriedade interessante da
linguagem Scala: mtodos que recebem apenas um argumento podem ser usados
com uma sintaxe no fixa. Desta forma, a expresso
df format now

apenas outra forma menos extensa de escrever esta expresso


df.format(now)

Isto pode ser um detalhe menor da sintaxe, mas possui consequncias importantes,
as quais sero exploradas com mais detalhes na prxima seo.
Para concluir esta seo sobre integrao com Java, voc deve notar que possvel
herdar a partir de classes Java e implementar interfaces Java diretamente em Scala.

Tudo um objeto

Scala uma linguagem orientada a objetos pura, no sentido de que tudo um objeto, incluindo nmeros e funes. Isso difere de Java, j que Java distingue tipos
primitivos (como Boolean e Int) de tipos de referncia, e no permite que sejam
manipulados funes como valores.

4.1 Nmeros so objetos


Como nmeros so objetos, eles podem ter mtodos. E, de fato, uma expresso
aritimtica como a abaixo:
1 + 2 * 3 / x

4.2

Funes so objetos

consiste exclusivamente de chamadas de mtodos, por que ela equivalente a expresso abaixo, como vimos na seo anterior:
1.+(2.*(3./(x)))

Isso quer dizer que +, *, etc. so identificadores vlidos em Scala.

4.2 Funes so objetos


Por mais surpreendente que possa parecer para um programador Java, funes tambm so objetos em Scala. Isso possibilita passar funes como argumentos e outras funcionalidades interessantes. A possibilidade de manipular funes como valores um dos mais interessantes paradigmas da chamada programao funcional.
Um exemplo muito simples de como isso pode ser til o uso de funes como
valores, para isso vamos considerar uma funo do tipo timer que deve executar
uma ao a cada segundo. De que forma passamos a ao para execuo? De
maneira simples, atravs de uma funo. Esse simples uso de "passar uma funo"
deve ser familiar para muitos programadores: usado em cdigos de interface de
usurio, para registrar as funes de callback que devem ser chamadas quando algum evento ocorre.
No exemplo a seguir, a funo de timer nomeada oncePerSecond, e recebe uma
funo de callback como argumento. O tipo desta funo escrito como () => unit
e este o tipo de todas as funes que no recebem argumentos e no retornam
nada (o tipo unit similar ao void do C/C++). A funo main deste programa simplesmente chama essa funo de timer com uma callback que imprime uma sentena no terminal. Em outras palavras esse programa ir imprimir eternamente a
sentena o tempo corre como um raio a cada novo segundo:
object Timer {
def oncePerSecond(callback: () => unit) {
while (true) { callback(); Thread sleep 1000 }
}
def timeFlies() {
println("o tempo corre como um raio...")
}
def main(args: Array[String]) {
oncePerSecond(timeFlies)
}
}

Note que, para imprimir esta sentena, ns precisamos usar o mtodo println sem
a necessidade do uso de System.out.

4.2.1 Funes annimas


O cdigo anterior foi simples de entender, e podemos o refinar ainda mais. Primeiro
de tudo necessrio entender que a funo timeFlies definida apenas para ser
passada como parmetro para a funo oncePerSecond. Nomear uma funo com
esta caractersticas desnecessrio, sendo mais interessante construir essa funo
no momento em que a passamos para a oncePerSecond. Isso possvel em Scala
atravs do uso do conceito de funes annimas, que so exatamente o que parecem: funes sem nome. Uma verso atualiada de nosso programa de timer que
usa uma funo annima no lugar de timeFlies pareceria com o abaixo:
object TimerAnonymous {
def oncePerSecond(callback: () => unit) {
while (true) { callback(); Thread sleep 1000 }
}
def main(args: Array[String]) {
oncePerSecond(() =>
println("o tempo corre como um raio..."))
}
}

A presena de uma funo annima neste exemplo revelada pelo smbodo de =>
que separa os argumentos da funo de seu corpo. Neste exemplo, a lista de argumentos est vazia, o que pode ser visto pelo par de parenteses sem nada dentro a esquerda da flecha. O corpo da funo o mesmo que tinhamos na extinta
timeFlies, do exemplo acima.

Classes

Como vimos acima, Scala uma linguagem orientada a objetos, e dessa forma possui o conceito de classes.2 Essas classes, em Scala, so declaradas usando uma sintaxe muito parecida com a do Java. Uma das diferenas mais marcantes que
classes em Scala podem ter parmetros. Isso ilustrado com a definio abaixo
de uma classe para nmeros complexos:
class Complex(real: Double, imaginary: Double) {
def re() = real
def im() = imaginary
}

Essa classe Complex recebe dois argumentos, que so a parte real e a parte imaginria de um nmero complexo. Estes argumentos devem ser passados no mo2

Para evitar chateaes: sabemos que algumas linguagens orientadas a objeto no possuem o
conceito de classes, mas Scala no uma dessas.

5.1

Mtodos sem argumentos

mento da criao de uma instncia da classe Complex, da seguinte maneira:


new Complex(1.5, 2.3)

A classe contm dois mtodos, chamados re e im, que do acesso a ambas as partes
do nmero.
Repare que o tipo de retorno desses dois mtodos no especificado explicitamente. Ele ser definido automaticamente pelo compilador, que olha os mtodos e
deduz que o valor de retorno de ambos um Double.
O compilador, porm, pode no estar sempre apto a saber qual o tipo de retorno,
e no h uma regra simples para saber qual o tipo que ele efetivamente usou. Na
prtica isso no um problema visto que o compilador sabe que s pode mudar o
tipo que no foi explicitamente passado. Como uma dica, o programador iniciante
em Scala devem tentar omitir tipos quando esses forem fceis de perceber no contexto, e ver como o compilador se comporta. Aps algum tempo o programador
ter um bom feeling sobre que tipo pode ou no omitir.

5.1 Mtodos sem argumentos


Um pequeno problema dos mtodos re e im que, para os chamar, deve-se usar um
par de parenteses vazios ao lado de seu nome, como pode ser visto abaixo:
object ComplexNumbers {
def main(args: Array[String]) {
val c = new Complex(1.2, 3.4)
println("imaginary part: " + c.im())
}
}

Seria interessante acessar a parte real e a parte imaginria como campos, sem a
necessidade de colocar esse par de parenteses ao lado do nome. Isso pode ser feito
em Scala atravs da definio de mtodos sem argumentos. Nossa classe Complex
pode ser reescrita como abaixo:
class Complex(real: Double, imaginary: Double) {
def re = real
def im = imaginary
}

5.2 Herana e polimorfismo


Todas as classes em Scala herdam de uma super-classe. Quando no informada
de qual super-classe dever herdar, como no nosso exemplo Complex, usado por
padro scala.Object.
Desta forma possvel sobrescrever mtodos herdados da super-classe. Em Scala

obrigatrio especificar que o mtodo est sendo sobrescrito atravs do uso do modificador override, para evitar sobrescritas acidentais. Por exemplo, nosso cdigo da
classe Complex pode ser ampliado com a redefinio do mtodo toString, herdado
da classe Object.
class Complex(real: Double, imaginary: Double) {
def re = real
def im = imaginary
override def toString() =
"" + re + (if (im < 0) "" else "+") + im + "i"
}

Classes case e localizao de padres

Um tipo de estrutura de dados que costuma aparecer em softwares a rvore. Por


exemplo, interpretadores e compiladores usualmente representam programas internamente como rvores; documentos XML so rvores; e diversos tipos de contineres so baseados em rvores.
Ns agora iremos examinar como rvores so representadas e manipuladas em
Scala atravs de um programa simples que simula uma calculadora. O objetivo
deste programa manipular expresses aritmticas simples, compostas de somas,
constantes do tipo inteiro e variveis. Dois exemplos dessas expresses so 1 + 2 e
(x + x) + (7 + y).
Ns precisamos primeiro decidir qual a representao que usaremos para as expresses. A mais natural uma rvores, onde os ns so as operaes (no nosso
caso, adio) e o restante so os valores (no caso constantes e variveis).
Em Java, uma rvore pode ser representada usando uma super-classe abstrata, e
uma sub-classe concreta por sub-classes concretas. Em linguagens de programao
funcionais ns podemos usar um tipo algbrico para o mesmo propsito. Scala
possui o conceito de classes case que um meio termo entre ambos. Abaixo voc
pode ver um exemplo onde definimos a rvore para nossa calculadora:
abstract class Tree
case class Sum(l: Tree, r: Tree) extends Tree
case class Var(n: String) extends Tree
case class Const(v: Int) extends Tree

Perceba que, pelo fato das classes Sum, Var e Const serem declaradas como classes
case, elas diferem de classes padro em vrios aspectos:
a palavra chave new no necessria para criar instncias dessas classes (quer
dizer que podemos escrever simplesmente Const(5) ao invs de new Const(5)),

6 Classes case e localizao de padres

funes do tipo getter so automaticamente definidas para os parmetros do


construtor (por exemplo, possvel pegar o valor do parmetro v de uma instncia chamada c da classe Const apenas com c.v),
a definio padro dos mtodos equals e hashCode so disponibilizadas, trabalhando com a structure das instncias e no com sua identidade.
uma definio padro para o mtodo toString tambm disponibilizada, e
imprime o valor na sua forma padro (por exemplo, a expresso x + 1 imprime Sum(Var(x),Const(1))),
instncias dessas classes podem ser decompostas atravs de busca por padro
como veremos mais a frente.
Agora que ns temos definidos os tipos de dados que representam nossa expresso
aritmtica ns podemos iniciar a definir os operadores para manipul-las. Nossas
expresses iro iniciar com uma funo que ir avaliar a expresso em um ambiente. O objetivo do ambiente dar valores as variveis. Por exemplo, a expresso
x + 1 avaliada em um ambiente que associa o valor 5 a varivel x, escreve {x 5},
dando 6 como resultado.
Dessa forma temos que encontrar um jeito de representar ambientes. Ns podemos, claro, usar algumas estruturas de dados associativas, como uma tabela hash,
mas ns podemos usar diretamente funes! Um ambiente nada mais do que
uma funo que associad um valor a uma varivel. O ambiente {x 5} mostrado
acima pode ser escrito como abaixo em Scala:
{ case "x" => 5 }

Esta notao define uma funo que, quando recebe a string x como um argumento,
retorna o inteiro 5, e gera uma exceo se for diferente disso.
Antes de escrever a funo de avaliao deixe-me dar um nome ao tipo dos ambientes. Ns podemos, claro, sempre usar o tipo String => Int para ambientes, mas
simplificaria o programa se ns introduzirmos um nome para este tipo, o que permite modificaes simples no futuro. Isso pode ser feito em Scala como mostrado
abaixo:
type Environment = String => Int

A partir de agora o tipo Environment (ambiente) pode ser usado como um apelido
para funes que vo de um String para um Int.
Ns podemos agora definir a funo de avaliao. Conceitualmente ela muito
simples: o valor de uma soma de duas expresses simplesmente o valor da soma
dessas expresses; o valor da varivel obtido diretamente pelo ambiente e o valor
da constante o valor da constante por s s. Expressar isso em Scala no mais
difcil:

10

def eval(t: Tree, env: Environment): Int = t match {


case Sum(l, r) => eval(l, env) + eval(r, env)
case Var(n)
=> env(n)
case Const(v) => v
}

Essa funo de avaliao funciona realizando uma busca de padro na rvore t.


Intuitivamente o significado de algumas dessas definies pode ficar mais claro:
1. a primeira verificao da rvore t o Sum, ento criada uma sub-rvore auxiliar a esquerda numa varivel chamada l e uma sub-rvores numa varivel
r, e ento segue com a avalio da expresso que est aps a flecha; esta expresso pode (e faz) uso das variveis seguidas pelo padro que aparece antesda flecha, l e r,
2. se a primeiraq verificao no for bem sucedida ento a rvore no um Sum,
ento verificado se t um Var; se o ento ele anexa o nome contido em
Var para uma varivel n e continua com a avaliao na expresso,
3. se a segunda verificao falha ento t no nem um Sum e nem um Var, ento
ele verifica se um Const, e se o for, ento ele anexa o valor contido no n em
Const na varivel v e continua a verificao,
4. finalmente, se todas as verificaes falharem, uma exceo lanada para
sinalizar a falha da busca por um padro; isso aconteceria aqui apenas se mais
de uma sub-classe de Tree fosse declarada.
Ns verificamos que a idia bsica da busca de padro tentar achar um valor numa
srie de padres, e quando o padro encontrado, extrair e nomear as vrias partes
do mesmo para, finalmente, avaliar um cdigo que tipicamente faz uso dessas partes
nomeadas.
Um programador experiente em orientaes a objetos pode ficar supreso por que
no definimos eval como um mtodo da classe Tree e suas subclasses. Ns poderamos
fazer isso sem problemas, visto que Scala possibilita a definio de mtodos em
classes case assim como em classes normais. Decidir usar a busca por padro ou
mtodos uma questo de gosto, mas isso possui implicaes importantes em relao a extensibilidade da aplicao:
quando usamos mtodos fica fcil de adicionar um novo tipo de n, bastando
para isto apenas defin-lo em uma sub-classe da Tree; Em contrapartida, adicionar uma nova operao para manipular a rvore uma tarefa tediosa, pois
isto requer modificaes em todas as subclasses de Tree
quando usamos busca por padro a situao invertida: adicionar um novo
tipo de n requer que modificao em todas as funes nas quais a busca
por pado atua, para ter o novo n devidamente; Em contrapartida, adicionar

6 Classes case e localizao de padres

11

uma nova operao fcil, precisando apenar defin-la como uma funo independente.
Para explorar bastante buscas de padres, vamos definir outra operao com expresses aritmticas: derivao simblica. O leitor deve lembrar as seguintes regras
referentes a esta operao:
1. a derivada de uma soma a soma de suas derivadas,
2. a derivada de qualquer varivel v um se v a varivel relativa na qual a
derivao ocorre seno ser zero,
3. a derivada de uma constante zero.
Estas regras podem ser traduzidas quase que literalmente para cdigo Scala, para
obter a seguinte definio:
def derive(t: Tree, v: String): Tree = t match {
case Sum(l, r) => Sum(derive(l, v), derive(r, v))
case Var(n) if (v == n) => Const(1)
case _ => Const(0)
}

Esta funo introduz dois novos conceitos relacionados busca de padres. Primeiramente, a expresso case para as variveis possui uma proteo, uma expresso que
segue a palavra-chave if. Esta proteo previne que a busca de padres tenha
sucesso a no ser que esta expresso seja verdadeira. Aqui usado para ter certeza
que retornaremos a constante 1 apenas se o nome da varivel que est sendo derivada
o mesmo o mesmo da varivel de derivao v. A segunda nova funcionalidade
da busca de padres usada aqui o caractere curinga, escrito como underline, no
qual uma busca de padres de qualquer valor, sem precisar nome-lo.
Ns ainda no exploramos todo o poder da busca de padres, mas iremos parar
por aqui para manter este documento resumido. Ainda queremos ver como as duas
funes acima funciona num exemplo real. Para este propsito vamos escrever uma
simples funo main, na qual realiza vrias operaes sobre a expresso (x + x) +
(7 + x): primeiro computado seu valor no ambiente {x 5, y 7}, para ento
computar sua derivada relativa a x e ento y.
def main(args: Array[String]) {
val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y")))
val env: Environment = { case "x" => 5 case "y" => 7 }
println("Expression: " + exp)
println("Evaluation with x=5, y=7: " + eval(exp, env))
println("Derivative relative to x:\n " + derive(exp, "x"))
println("Derivative relative to y:\n " + derive(exp, "y"))
}

12

Executando esse programa ns temos a sada esperada:


Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y)))
Evaluation with x=5, y=7: 24
Derivative relative to x:
Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0)))
Derivative relative to y:
Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1)))

Examinando a sada, ns podemos ver que o resultado da derivada deve ser simplificada antes de ser apresentada ao usurio. Definir uma funo de simplificao
bsica, usando busca de padres, uma problema interessante(mas surpreendentemente capcioso) e deixado como um exerccio para o leitor.

Mixins

Alm da herana de cdigo de uma superclasse, uma classe Scala tambm pode
importar cdigo de um ou vrios mixins.
Talvez a forma mais fcil para um programador java entender o que so mixins
v-los como interfaces na qual podem tambm conter cdigo. Em Scala, quando
uma classe sub-classe de mixin, ela implementa aquela interface mixin e herda
todo o cdigo contido no nele.
Para ver a utilidade dos mixins, veremos um exemplo clssico: objetos ordenados.
Geralmente til ser capaz de comparar objetos de uma dada classe atravs delas
mesmas, como por exemplo para orden-las. Em Java, objetos nos quais so comparveis implementam a interface Comparable. Em Scala ns podemos fazer um
pouco melhor do que em Java, definindo nosso equivalente de Comparable como
um mixin, no qual ns chamaremos de Ord
Ao comparar objetos, seis diferentes predicados podem ser teis: menor, menor ou
igual, igual, no igual, maior ou igual e maior. Contudo, definindo todos ele trabalhoso, especialmente considerando que quatro desses seis podem ser expressos
usando os dois remanescentes, isto , dado os predicados igual e menor (por exemplo), um pode expressar os outros. Em Scala, todas estas observaes podem ser
facilmente capturadas pela seguinte declarao do mixin:
trait
def
def
def
def
}

Ord {
< (that:
<=(that:
> (that:
>=(that:

Any):
Any):
Any):
Any):

Boolean
Boolean = (this < that) || (this == that)
Boolean = !(this <= that)
Boolean = !(this < that)

7 Mixins

13

Esta definio cria um novo tipo chamado Ord, no qual atua o mesmo papel da
interface Comparable em Java e padroniza as implementaes dos trs predicados
em termos de um quarto abstrato. Os predicados para igualdade e diferena no
aparecem aqui pelo motivo de que so padres presentes em todos os objetos.
O tipo Any que usado acima, um super-tipo de todos os outros tipos em Scala.
Ele pode ser visto como uma verso mais geral do tipo Object em Java, mas tambm
um super-tipo dos tipos bsicos como Int, Float, etc.
Para fazer objetos de uma classe serem comparveis, mais do que suficiente apenas definir os predicados que testam igualdade e inferioridade, e ento misturar o
cdigo de Ord classe. Como um exemplo, definiremos uma classe Date, representando datas no calendrio gregoriano. As datas so compostas por um dia, um ms
e um ano e so representados como inteiros. Comearemos a definio da classe
Date como o seguinte:
class
def
def
def

Date(y: Int, m: Int, d: Int) extends Ord {


year = y
month = m
day = d

override def toString(): String = year + "-" + month + "-" + day

A parte importante aqui a declarao de extends Ord que segue do nome da


classe e dos parmetros. Isto declara que a classe Date uma sub-classe da classe
Ord como mixin.
Ns redefinimos o mtodo equals que foi herdado de Object, ento ele comparar
corretamente as datas, comparando seus campos individualmente. A implementao padro de equals no deve ser usada, pois como em Java ele compara os
mtodos fisicamente. Ns chegamos na seguinte definio:
override def equals(that: Any): Boolean =
that.isInstanceOf[Date] && {
val o = that.asInstanceOf[Date]
o.day == day && o.month == month && o.year == year
}

Este mtodo faz o uso dos mtodos j predefinidos isInstanceOf e asInstanceOf. O


primeiro, isInstanceOf, corresponde ao operador instanceof do Java e retorna verdadeiro se, e apenas se, o objeto no qual foi aplicado, uma instncia do tipo dado.
O segundo, asInstanceOf, corresponde ao operador de cast do Java: se o objeto
uma instncia do tipo dado, ele visto como tal, seno uma exceo ClassCastException
lanada.
Finalmente, o ltimo mtodo para definir o predicado no qual testa a inferioridade, como a seguir. Este faz uso de outro mtodo pr-definido, error, que lana
uma exceo junto a dada mensagem de erro.

14

def <(that: Any): Boolean = {


if (!that.isInstanceOf[Date])
error("cannot compare " + that + " and a Date")
val o = that.asInstanceOf[Date]
(year < o.year) ||
(year == o.year && (month < o.month ||
(month == o.month && day < o.day)))
}

Assim completamos a definio da classe Date. Instncias dessa classe podem ser
vistas como datas ou como objetos comparveis. Alm do mais, elas todas definem
os seis predicados de comparao mencionados acima: equals e <, pois eles aparecem diretamente na definio da classe Date, e os outros por causa da herana do
mixin Ord.
Mixins so teis em outras situaes a mais do que as mostradas aqui, com certeza,
mas discutir todas as suas aplicaes est fora do escopo deste documento.

Generalizao

A ltima caracterstica de Scala que exploraremos neste tutorial a generalizao.


Programadores Java devem estar bem prevenidos sobre os problemas causados pela
falta de generalizao em sua linguagem, uma deficincia que abordada no Java
1.5.
Generalizao a habilidade de escrever cdigo parametrizado por tipos. Como exemplo, um programador escrevendo uma biblioteca para listas encadeadas encontra o problema de decidir qual tipo dar para os elementos da lista. Desde que esta
lista foi concebida para ser usada em diferentes contextos, no possvel decidir
que o tipo dos elementos tero de ser, por exemplo, Int. Isto pode ser totalmente
arbitrrio e muito restritivo.
Programadores Java contornam isto usando Object, que o super-tipo de todos os
objetos. Contudo, esta soluo est longe da ideal, desde que no funcionar para
os tipos bsicos (Int, Long, Float, etc.) e implicando que vrios type casts tero de
ser inseridos pelo programador.
Scala torna possvel definir classes (e mtodos) genricos para resolver este problema. Vamos examinar isto com um exemplo do container de classe mais simples
possvel: uma referncia na qual pode ser vazia ou apontar para um objeto de algum
tipo.
class Reference[a] {
private var contents: a = _
def set(value: a) { contents = value }

9 Concluso

15

def get: a = contents


}

A classe Reference parametrizada por um tipo, chamado a, que o tipo do seu


elemento. Este tipo usado no corpo da classe como o tipo da varivel contents,
do argumento do mtodo set e do tipo de retorno do mtodo get.
O exemplo do cdigo acima mostra variveis em Scala que no precisamos explicar
mais. Contudo interessante ver qua o valor dado inicialmente para a varivel _,
que representa o valor padro. Este valor padro 0 para os tipos numricos, false
para o tipo booleano, () para o tipo unit e null para todos os tipos de objetos.
Para usar esta classe Reference, alguem precisa especificar qual o tipo para se usar
no parmetro de tipo a no qual o tipo contido pela clula. Como exemplo, ao criar
e usar uma clula guardando um inteiro, algum deve escrever assim:
object IntegerReference {
def main(args: Array[String]) {
val cell = new Reference[Int]
cell.set(13)
println("Reference contains the half of " + (cell.get * 2))
}
}

Como pode ser visto no exemplo, no necessrio fazer cast no valor retornado pelo
mtodo get antes de us-lo como um inteiro. Tambm no ser possvel guardar
nada alm de um inteiro naquela clula particularmente, pois foi declarada para
guardar um inteiro.

Concluso

Este documento apresenta uma rpida viso geral da linguagem Scala com alguns
poucos exemplos. Caso deseje aprofundar-se mais recomendamos que leia o Scala
By Example, que possui muito mais exemplos, e consulte a Scala Language Specification quando necessrio.

10

Sobre a traduo

Este documento foi traduzido por Marcelo Castellani e Thiago Rocha, membros da
lista de discusso Scala-Br. Para participar visite a pgina a seguir:
http://groups.google.com/group/scala-br

Você também pode gostar