Scala é uma linguagem de programação de propósito geral projetada para expressar padrões de programação comuns de uma forma concisa, elegante e de tipagem segura (type-safe). Ela integra os recursos de linguagens orientadas a objetos e funcional, permitindo que programadores Java e de outras linguagens sejam mais produtivos e práticos no que concerne o quesito desenvolvimento de software.

Um dos grandes atrativos da linguagem, que é percebido logo de cara por programadores que já tenham experiência em outras linguagens orientadas a objetos, é a quantidade de código gerado. O tamanho de código é normalmente reduzido pela metade ou a um terço em relação a uma aplicação Java equivalente.

No geral, um dos fatores mais importantes para empresas de tecnologias apontarem a linguagem Scala como uma substitua direta do Java é a produtividade. Além disso, quesitos como capacidade de desenvolvimento, escalabilidade e confiabilidade são sempre levados em consideração na hora da avaliação de substituição.

Scala combina os paradigmas de programação orientada a objetos e funcionais, utilizando uma sintaxe concisa que é totalmente compatível com Java e é executado na JVM. O suporte para o estilo de programação funcional, e, em particular, expressões lambda que não se esperava até o JavaSE 8, podem ajudar a reduzir a quantidade de código verboso (boiler plate code) que você é obrigado a escrever, sem dúvida, concentrando-se em tarefas mais específicas e direcionadas. Neste artigo forneceremos uma introdução à Scala, visando um contato inicial com a linguagem, principalmente para quem já desenvolve em Java.

Primeiro Exemplo

Como primeiro exemplo, vamos implementar o programa OlaMundo padrão para demonstrar o uso das ferramentas Scala sem saber muito sobre a linguagem. O código fonte resultante disso se encontra na Listagem 1.

Listagem 1. AloMundo em Scala

object AloMundo {
  def main(args: Array[String]) {
  println("Alô Mundo!")
  }
  }

A estrutura consiste de um método chamado “main” que leva os argumentos de linha de comando e um array de strings como parâmetro; o corpo deste método consiste em uma única chamada para o predefinido método “println()” com a mensagem como argumento. O método principal não retorna um valor (que é um método de procedimento). Portanto, não é necessário declarar um tipo de retorno.

O que é menos familiar para os programadores Java é a declaração do objeto que contém o método principal. Tal declaração introduz o que é comumente conhecido como um objeto “singleton”, que é uma classe com uma única instância. A declaração acima declara, assim, tanto uma classe chamada AloMundo assim como uma instância dessa classe, também chamada AloMundo.

Esta instância é criada sob demanda na primeira vez que é usada. Além disso, é possível observar que o método principal não é declarado como estático. Isso ocorre porque os recursos estáticos (métodos ou campos) não existem na Scala. A saída então para declarar esse tipo de comportamento usando a linguagem Scala se dá através da implementação dos Singleton’s, conforme falado anteriormente.

E aí fica a pergunta: Como executar este código? Sim, porque para os programadores Java todo o processo de codificação da classe, compilação (e interpretação implícita) acontecem antes que o resultado possa ser visto. Isso sem falar em toda a complexidade de configurar as velhas variáveis de ambiente para que o seu Sistema Operacional reconheça o Java instalado e encontre os caminhos de classe (classpath) correspondentes para executar tudo junto. A Scala também segue toda essa complexidade de passos e configurações?

A resposta é sim. E veremos por que.

Instalando a Scala

Instalar a Scala no seu computador é relativamente simples, considerando o fato de que já tenha tido experiência anteriormente em configuração de variáveis de ambiente no Windows, por exemplo.

O primeiro passo é fazer o download da Scala no site oficial (vide seção Links no final do artigo).

Ao acessar o mesmo, você será levado à página oficial de download da versão em português da Scala, tal como na Figura 1.

Figura 1. Página de download da Scala

Conforme exibido na mesma, você terá três opções de configuração disponíveis, são elas:

  1. Download do binário, executável para o seu Sistema Operacional específico;
  2. Download do Typesafe Activator, uma ferramenta para browser (browser-based) capaz de prover recursos de utilização da linguagem, como uma espécie de plugin interno;
  3. Assim como o Java, você também terá em mãos as ferramentas IDEs que proveem um ambiente mais completo e integrado para o uso da linguagem. Atualmente, é possível usar Scala no Scala IDE (espécie de IDE baseada no Eclipse), IntelliJ IDEA através de um plugin, e Netbeans também com o plugin em sua versão 7.3+.

Neste artigo usaremos a versão para SO, para evitar adentrar muito no quesito ferramentas gráficas e/ou plug-ins. Clique no link e faça o download do executável.

Obs.: Note que na mesma página de download existem algumas notas que avisam sobre a dependência do Java, em sua versão 1.6 ou superior. Se você não tem Java instalado na máquina, faça o download e instalação do mesmo e, só assim, prossiga com o tutorial.

A instalação também é bem simples e intuitiva. Execute o instalador e selecione as opções “Next” sem mudar nenhuma configuração padrão, não se esquecendo de aceitar os termos da licença.

Pronto, a Scala está pronta para ser usada no seu Windows.

O processo para usar a linguagem gira em torna dos comandos que agora o seu Windows reconhece. Outra parte semelhante à forma como o Java trabalha é o quesito compilação e execução. Para compilar o código fonte Java, é necessário que o desenvolvedor tenha salvado todo o fonte em um arquivo de extensão “.java”. Além disso, o nome do arquivo deve ser o mesmo da classe que foi declarada dentro do mesmo. Para compilar o arquivo executamos o comando “javac NomeDaClasse.java” no prompt de comando do Windows para que o Java gere o arquivo de resultado “NomeDaClasse.class” (Observe que tudo isso deve ser feito dentro do diretório onde suas classes estão); e para executar o binário basta executar o comando subsequente “java NomeDaClasse” e ver o resultado.

Na Scala, funciona da mesma forma, só mudam os comandos: “scalac NomeDoObjeto.scala” e “scala NomeDoObjeto”, respectivamente.

Como forma de teste, executemos então o exemplo da Listagem 1. Abra o cmd do seu Windows e para conferir se a Scala realmente foi instalada corretamente, digite a linha de comando:

C:/> scala –version

Esse comando irá exibir qual a versão da Scala que está configurada na sua máquina. O resultado será algo como:

Scala code runner version 2.10.3 -- Copyright 2002-2013, LAMP/EPFL

Após verificar se a linguagem está ok, é hora de executar o exemplo. Abra o notepad ou algum editor de texto e cole o código do exemplo da Listagem 1. Salve o arquivo com o mesmo nome do “object”, com extensão “.scala”.

No cmd, novamente, vamos digitar o comando para entrar no caminho de diretórios onde você salvou o arquivo scala. Se salvou no Desktop, por exemplo, digite:

C:/> cd C:\Users\Diogo\Desktop

E logo após digite o comando para compilar o arquivo:

scala AloMundo.scala

O resultado será a criação do arquivo AloMundo.class no mesmo diretório onde se encontra o arquivo scala. Isso mesmo, o resultado é um .class porque o compilador da linguagem usa o mesmo conceito de geração de bytecode que o Java. Para executar o exemplo basta executar o comando:

scala AloMundo

O resultado pode ser visualizado na Figura 2.

Figura 2. Executando AloMundo em Scala

Além disso, o mesmo binário da Scala permite ao desenvolvedor executar comandos da linguagem de forma estrutural através do próprio prompt de comando do Windows. Para isso, digite apenas a palavra “scala” no console e você verá uma mensagem de boas vindas para a Scala. Na verdade, é como se o prompt tivesse aberto um sub-prompt do próprio Scala para executar comandos específicos da linguagem. Prova disso é que agora, ao invés de ver o cursor pulsando após um “C:\> _”, você verá um “scala> _”.

Fazendo apenas um teste rápido, execute alguma operação aritmética no mesmo console, sem a necessidade de declarar variáveis ou coisas do tipo, como por exemplo:

scala> 2 + 8

E dê um enter. O resultado será:

res0: Int = 10

Isso significa dizer que o compilador Scala processou a operação aritmética e associou o resultado, do tipo inteiro, ao valor 10. O termo “res0” vem de “response0” e é referente a um contador que verificará o número de execuções feitas no mesmo processo do prompt.

Alguns dos exemplos que usaremos abaixo para entender melhor a linguagem podem ser testados usando esse recurso do prompt da Scala.

Interagindo com o Java

Um dos pontos fortes da Scala é a facilidade na interação com código Java. Basta que você se acostume à forma como a linguagem lida com a sintaxe antes “acostumada” do Java. Todas as classes do pacote java.lang são importadas por padrão, enquanto outros precisam de ser importados explicitamente, da mesma forma que já funciona no Java.

Por exemplo, um desenvolvedor deseja fazer um código para obter e formatar a data atual de acordo com as convenções usadas em um país específico, como a França ou Alemanha. Algumas bibliotecas de classes do Java definem classes de utilitários poderosas, como as classes Date e DateFormat.

Considerando que Scala interage perfeitamente com Java, não há necessidade de implementar classes equivalentes nas bibliotecas da Scala, uma vez que nós podemos simplesmente importar as classes dos correspondentes a Java.

Veja na Listagem 2 o código que usaríamos para fazer isso buscando a data em um padrão alemão.

Listagem 2. Código exemplo para criação e formatação de objeto data em alemão em Java.

import java.text.DateFormat;
  import java.util.Date;
  import java.util.Locale;
   
  public class DataAlemao {
       public static void main(String[] args) {
            Date dataHoje = new Date();
            DateFormat dt = 
            DateFormat.getDateInstance(DateFormat.LONG,                    
              Locale.GERMAN);
            System.out.println(dt.format(dataHoje));
       }
  }

O resultado da execução desse código seria algo como:

10. Dezember 2013

Agora veja como nosso código se transformaria usando o mesmo algoritmo, só que agora em Scala (Listagem 3).

Listagem 3. Código de criação e formatação da data em Scala

import java.util.{Date, Locale}
  import java.text.DateFormat
  import java.text.DateFormat._
  object DataAlemao {
       def main(args: Array[String]) {
            val dataAtual = new Date
            val df = getDateInstance(LONG, Locale.GERMAN)
            println(df format dataAtual)
       }
  }

E o resultado da execução seria o mesmo. Note que o primeiro efeito que temos é que tem menos código na Listagem 3 em comparação com a Listagem 2.

Declarações de importação na Scala são muito parecidas com as importações do Java, no entanto, são mais potentes. Várias classes podem ser importadas a partir do mesmo pacote, colocando-as entre chaves, como na primeira linha. Outra diferença é que ao importar todas as classes de um pacote, a Scala usa o caractere sublinhado (_) em vez do asterisco (*), como é no Java. Isso porque o asterisco é um identificador reservado da linguagem Scala.

A declaração de importação na terceira linha, portanto, importa todos os membros da classe DateFormat. Isso faz com que o método getDateInstance estático e o campo estático LONG estejam diretamente visíveis.

Dentro do método principal, primeiro criamos uma instância da classe Date do Java, que por padrão contém a data atual. Em seguida, vamos definir um formato de data usando o método estático “getDateInstance” que nós importamos anteriormente . Por fim, imprimimos a data atual formatada de acordo com a instância DateFormat localizada. Esta última linha mostra uma propriedade interessante da sintaxe da Scala: métodos recebendo um argumento podem ser usados com uma sintaxe infixo. Isto é, a expressão:

df format dataAtual

é apenas outra maneira, um pouco menos detalhada de escrever a expressão:

df.format (dataAtual) 

Isto pode parecer um detalhe sintático menor, mas tem consequências importantes.

Recursos como herança e utilização de interfaces também podem ser usados diretamente em comunicação com o Java.

E aí o leitor pode se fazer a pergunta: “Ok, mas como poderíamos deixar o exemplo mais brasileiro? Sim, pois na classe Locale não consta nenhuma constante BRAZIL, por exemplo... ”.

Simples, pois agora que você já tem domínio sobre a forma que podemos usar os métodos Java na Scala, nossa implementação ficaria semelhante à da Listagem 4.

Listagem 4. Implementação da criação da data em padrão brasileiro

import java.util.{Date, Locale}
  import java.text.DateFormat
  import java.text.SimpleDateFormat
  import java.text.DateFormat._
  object DataBrasil {
     def main(args: Array[String]) {
        val now = new Date
        val df = new SimpleDateFormat("dd/MM/yyyy")
        println(df format now)
     }
  }

Quando os métodos Java necessitam passar valores de parâmetros, então o uso pode ser feito da mesma forma que na linguagem original.

O resultado da execução será:

10/12/2013

Objetos em Scala

Tudo é um objeto em Scala. Essa frase define algo que muitas vezes é confundido com o velho conceito de Orientação a Objetos que aprendemos com Java ou outras linguagens do tipo.

Scala é uma linguagem puramente orientada a objetos, no sentido de que tudo é um objeto, incluindo números ou funções. Ela difere do Java nesse quesito, uma vez que o Java distingue tipos primitivos (como boolean e int) de tipos de referência, e não permite que funções sejam manipuladas como valores.

O conceito de números na Scala difere do quesito “tipos primitivos” que o Java implementa e está mais relacionado à forma como a API Java encapsula esses mesmos tipos, através das classes wrapper. Como os números são objetos em Scala, isso significa que eles têm métodos e podem fazer determinadas ações que objetos implementados também fazem. Ao executar a chamada a expressões aritméticas, você estará automaticamente chamando as mesmas chamadas às funções correspondentes. Isso também significa dizer que os operadores que usamos para executar tais expressões são identificadores válidos na Scala.

Na expressão abaixo, por exemplo, conseguimos identificar uma operação que é expressa em um resultado final, conforme vimos anteriormente no console:

1. + 2

Que é o mesmo que:

1.+(2) ou (1).+(2) 

Obs.: O operador ponto (“.”), neste caso, assume a postura de transformar o valor numérico em um ponto flutuante. É o equivalente ao d ou f no final dos valores, no Java.

As funções que conhecemos na Scala (ou métodos, mais comumente no Java) também são objetos.

Por conseguinte, é possível passar funções como argumentos para armazená-las em variáveis, e devolvê-las a partir de outras funções. Esta capacidade de manipular as funções como valores é considerada a pedra angular de um paradigma de programação muito interessante chamado “programação funcional”.

Como um exemplo muito simples do por que isso pode ser útil para usar funções como valor, vamos considerar uma função de temporizador, cujo objetivo é realizar alguma ação a cada segundo. Como vamos passar a ação a ser executada? Logicamente, como uma função. Este tipo de função de passagem deve ser familiar para muitos programadores: é muitas vezes usado em código de interface de usuário, para registrar as funções de call-back, que são chamadas quando algum evento ocorre, os famosos listeners.

No programa seguinte, a função do temporizador é chamado umPorSegundo, e recebe uma função call-back de retorno como argumento. O tipo desta função está escrito como “() => Unit” e é o tipo de todas as funções que não tem argumentos e não retornam nada também (o tipo Unit é semelhante ao void do C/C++/Java). A principal função deste algoritmo é simplesmente chamar essa função timer com um call-back que imprime uma sentença no console.

Em outras palavras, este algoritmo imprime sem parar a frase "O tempo passa e o homem não percebe. (Dante Alighieri)" a cada segundo.

Veja o código correspondente na Listagem 5.

Listagem 5. Código de execução do temporizador por segundo

object Timer {
     def umPorSegundo(callback: () => Unit) {
        while (true) { callback(); Thread sleep 1000 }
     }
     def oTempoPassa() {
        println("O tempo passa e o homem não percebe. 
        (Dante Alighieri)")
     }
     def main(args: Array[String]) {
        umPorSegundo(oTempoPassa)
     }
  }

Execute também o mesmo passo a passo para compilar o arquivo e executar, não esquecendo de renomear os nomes do arquivo e object.

Observe também que se você não conseguir executar o arquivo com o comando de execução aprendido até então, provavelmente é porque faltou a definição de classpath. Mude o seu comando para o mostrado abaixo:

scala -classpath . Timer

E o resultado será o exibido na Figura 3. Use Ctrl+C para parar a execução.

Figura 3. Resultado de execução do algoritmo de Timer

Não esqueça de que tudo explicado aqui leva em consideração os seus conhecimentos prévios na linguagem Java, haja vista a “necessidade” de conhecê-la para usar Scala.

A grande maioria dos programadores que usa Scala veio antes do Java, e mais raramente o contrário. Scala é mais simples, rápida e menos verbosa no quesito codificação. Entretanto, a definição de uso da mesma deve sempre levar em consideração diversos fatores, tais como finalidade do projeto, recursos disponíveis, nível de maturidade da equipe, etc. Tais fatores, dependendo da complexidade do projeto, levam à linguagem ainda ser muito substituída pelo Java. Algo que está mudando com o tempo.

Links

http://www.scala-lang.org/download/