A máquina virtual Java fornece um tempo de execução universal e de alto desempenho para uma variedade de linguagens populares além do Java. Neste artigo, veremos os pontos fortes característicos e os casos de uso comuns de quatro das linguagens JVM mais populares: Kotlin, Scala, Groovy e Clojure.

Kotlin

Kotlin é uma linguagem moderna que tem visto uma onda de entusiasmo dos desenvolvedores nos últimos anos. Essa popularidade se deve em grande parte à sua sintaxe altamente expressiva, que inclui suporte à programação funcional e orientada a objetos, mas não para por aí. Kotlin é interoperável com Java e inclui ferramentas multiplataforma e compilação entre linguagens. Como outras linguagens JVM, você pode usar GraalVM para compilar Kotlin em binários nativos para implantação altamente otimizada com excelente uso de recursos de início, parada e tempo de execução.

Em 2019, o Google identificou o Kotlin como a linguagem preferida para o desenvolvimento do Android, um voto de confiança que impulsionou sua popularidade entre os desenvolvedores.

Outro fator na força do Kotlin é o apoio da JetBrains, o criador do IDE IntelliJ. A JetBrains manteve e refinou consistentemente o Kotlin. Esse investimento garantiu a estabilidade do Kotlin, ao mesmo tempo que o manteve na vanguarda da inovação, qualidades que os desenvolvedores apreciam.

Por ser 100% interoperável com Java, os desenvolvedores e organizações Java podem adotar o Kotlin gradualmente. É fácil para um desenvolvedor Java se sentir confortável com Kotlin e vice-versa. Também não é difícil manter os dois idiomas na cabeça. Para desenvolvedores Java experientes, Kotlin parece uma versão expandida do Java. E mesmo que você não conheça Java, você ainda pode se tornar um especialista em Kotlin.

Kotlin obviamente brilha para uso no Android, mas também é popular em outras áreas, incluindo o desenvolvimento no lado do servidor. Kotlin é adequado para desenvolver DSLs (linguagens específicas de domínio). Uma delas, o Kotlin HTML DSL, é uma linguagem de modelagem poderosa e integrada do lado do servidor para a web.

Um dos ativos mais conhecidos do Kotlin é seu recurso de segurança nula, que permite minimizar a ocorrência de NullPointerExceptionS. Tipos padrão como String não pode ser inicializado nulla menos que você permita explicitamente usando o modificador anulável (String?). Ao usar tipos anuláveis, o compilador não permite o acesso sem uma verificação de segurança. Kotlin também fornece o operador de ponto seguro para nulos (?.), que é semelhante ao operador de cadeia opcional em JavaScript. Aqui está uma olhada em Kotlin usando o ?: operador para fornecer um valor padrão ao verificar:

val length = middleName?.length ?: 0

Neste exemplo, se middleName é nulo, length será definido como 0.

Outro recurso matador é corrotinasque fornece uma maneira estruturada de gerenciar operações simultâneas. As corrotinas de Kotlin são inspiradas nas goroutines de Go e também serviram de inspiração para o novo modelo de simultaneidade estruturada do Java. Este exemplo mostra como uma corrotina Kotlin pode ser usada para fornecer sintaxe síncrona para lógica assíncrona:

import kotlinx.coroutines.*

fun main() = runBlocking { // main coroutine
    // Launch a new coroutine
    launch {
        delay(1000L)       // suspend for 1 second
        print("InfoWorld!")  // Print after delay
    }

    print("Hello,")      // The main coroutine continues 
}

Nós apenas arranhamos a superfície das habilidades do Kotlin, mas esses exemplos devem dar uma ideia de por que ele se tornou tão popular entre os desenvolvedores. Como linguagem principal, o Kotlin aumentou enormemente o poder e o alcance da JVM.

Veja também: Kotlin para desenvolvedores Java.

escala

Scala se diferencia de outras linguagens JVM ao tornar a programação funcional fundamental e implementá-la rigorosamente. Como resultado, os desenvolvedores que preferem a programação funcional e desejam aproveitar a JVM frequentemente recorrem ao Scala. Embora não seja enfatizado, Scala também possui forte suporte para programação orientada a objetos.

Scala é muito popular para processamento de dados em grande escala, alto rendimento e em tempo real. É a linguagem do Apache Spark, a plataforma distribuída para streaming de big data, processamento em lote, análise, aprendizado de máquina e muito mais. O uso extensivo e excelente da capacidade do Scala pelo Spark de unir fluxos de eventos com operadores funcionais é outro impulsionador poderoso para a adoção do Scala.

A correspondência de padrões é um dos recursos de programação funcional mais populares do Scala. Aqui está um exemplo da sintaxe tipo switch do Scala para controle de fluxo:

case class Message(sender: String, body: String)

val notification: Any = Message("Ada Lovelace", "Hello, InfoWorld!")

notification match {
  case Message(sender, body) => println(s"Message from $sender: $body")
  case "Ping"                => println("Received a Ping")
  case _                     => println("Unknown notification type")
}

Isso fornece uma ramificação se notification é um tipo de mensagem e nos permite definir uma função que recebe as propriedades dessa mensagem. Se notification é um String contendo “Ping”, vai para o segundo caso, e o caractere sublinhado define o padrão. A beleza dessa construção é que tudo acontece dentro do paradigma da programação funcional.

Scala também enfatiza a imutabilidade, outro princípio da programação funcional. A imutabilidade torna o software mais simples e menos sujeito a erros. No Scala, a principal palavra-chave de declaração de variável é valque é uma constante, e coleções integradas como List, Vectore Map são todos imutáveis. Você modifica as coleções usando operações funcionais como filterque criam novas coleções.

Scala também é muito forte em concorrência, empregando atores em um sistema de programação poderoso e de estilo reativo. O modelo de ator do Scala forma a base da renomada estrutura Akka, um conjunto de bibliotecas para computação distribuída e multithread.

Scala também possui um sistema de tipos sofisticado que oferece suporte a casos de uso avançados. Aqui está um exemplo do tipo trait, que combina uma classe abstrata e uma interface. O tipo trait permite que as classes descendam de múltiplos ancestrais com membros abstratos e concretos:

trait Speaker {
  def speak(): String 
  
  def announce(message: String): Unit = { 
    println(message)
  }
}

class Dog extends Speaker {
  override def speak(): String = "Woof!"
}

class Person(name: String) extends Speaker {
  override def speak(): String = s"Hello, my name is $name."
}

@main def main(): Unit = {
  val sparky = new Dog()
  val ada = new Person("Ada")

  println(s"The dog says: ${sparky.speak()}") 

  println(s"The person says: ${ada.speak()}") 

  ada.announce("I am learning about traits!") 
}

Observe que o Speaker trait possui métodos concretos e abstratos, e as classes que o estendem podem estender mais de um trait, o que não é possível com uma classe abstrata.

Há mais no Scala, é claro, mas esses exemplos dão uma ideia disso.

Legal

Groovy é a alternativa original da JVM. É uma linguagem de script altamente dinâmica, popular por sua sintaxe simples e de baixa formalidade. É a linguagem do onipresente gerenciador de compilação Gradle e é frequentemente usada como uma linguagem adesiva ou quando um aplicativo precisa de pontos de extensão personalizáveis. Também é conhecido por sua capacidade de definir DSLs.

Para desenvolvedores vindos de Java, Groovy parece uma versão de Java que tem alguns padrões e formalidades removidos. Groovy é principalmente um superconjunto de Java, o que significa que a maior parte do Java também é Groovy válido.

Groovy também é a linguagem da estrutura de teste Spock.

Groovy dispensa ponto-e-vírgula “desnecessário” e fornece automaticamente variáveis ​​não declaradas para scripts (conhecidas como vinculação de script). Isso é especialmente útil para extensões de aplicativos e DSLs, onde a linguagem host (particularmente Java) cria um contexto para o script Groovy e os usuários podem criar funcionalidades sem declarar variáveis.

Este exemplo oferece uma amostra do sabor simplificado do Groovy:

def list = (1, 2, 3, 4, 5)

def doubled = list.collect { it * 2 }
println("Doubled: " + doubled) //-> Doubled: (2, 4, 6, 8, 10)

def evens = list.findAll { it % 2 == 0 }
println("Evens: " + evens) //-> Evens: (2, 4)

Aqui você pode ver o tratamento de coleta de baixa formalidade do Groovy, que é baseado em programação funcional.

Outro recurso popular do Groovy é sua digitação dinâmica e opcional. Você pode declarar um tipo de variável, mas não é necessário. Se você não declarar o tipo da variável, o Groovy gerenciará a variável com base em como ela está sendo usada, uma técnica conhecida como ducktyping. (JavaScript tem uma operação semelhante.)

Finalmente, Groovy suporta metaprogramação, que é algo como uma versão mais poderosa da API de reflexão Java.

Clojure

Por último, mas não menos importante, Clojure é descendente de Lisp, uma linguagem fundamental usada em aprendizado de máquina e processamento simbólico. Lisp influenciou muitas linguagens e ocupa um lugar especial para os aficionados por linguagens, graças à sua mistura única de sintaxe expressiva, porém simples, e à filosofia de “código como dados”.

Código como dados, também conhecido como homoiconicidade, significa que o código é representado como estruturas de dados na linguagem. Isso abre oportunidades de metaprogramação porque a representação do código pode ser carregada e manipulada diretamente como software.

O código como dados também cria possibilidades para macros poderosas, onde a macro entende a sintaxe do código que expande. Essa abordagem para macros é diferente de linguagens como C, onde as macros são textos simples, muitas vezes levando a erros furtivos.

Aqui está uma função simples na sintaxe semelhante a Lisp do Clojure:

;; Comments in Clojure use double semi-colons
(defn greet (name)
  (str "Hello, " name "!"))

Os blocos entre parênteses que você vê são uma característica do código que também é estrutura de dados. Os parênteses indicam uma coleção (uma lista) e as funções são definidas e chamadas usando uma lista (por exemplo, palavras-chave, nomes de funções, argumentos).

Clojure também é conhecido por seu forte modelo de simultaneidade, sendo construído desde o início para simplificar o gerenciamento de estado em vários threads. O foco do Clojure na imutabilidade e no excelente suporte para transições de estado gerenciadas fazem dele uma linguagem simultânea completa. Ele se concentra na imutabilidade em vez de orquestrar estados mutáveis ​​entre threads, o que deixaria espaço para erros. Clojure também inclui um modelo de agente reativo para lidar com estado mutável e simultaneidade.

Clojure é uma linguagem altamente estruturada e refinada. É rigorosamente funcional em sua filosofia e oferece um poder significativo ao desenvolvedor. Essas qualidades no design e na execução do Clojure o tornaram uma escolha muito respeitada entre os programadores.

Conclusão

As quatro linguagens descritas aqui são as estrelas do universo das linguagens alternativas JVM, mas existem muitas outras. Em particular, existem versões JVM de linguagens convencionais, como jRuby e Jython.

Kotlin se tornou uma linguagem dominante por si só e recentemente entrou no top 20 do Tiobe. Mas todas as quatro linguagens trazem pontos fortes em áreas específicas. E todos eles demonstram o poder da própria JVM.

Aqui está uma olhada nas características de alto nível dos quatro idiomas:

Linguagem Paradigma Curva de aprendizado Caso de uso matador Valores fundamentais
Kotlin OOP, funcional (pragmático) Fácil Aplicativos Android Pragmatismo, segurança
escala Funcional, OOP (rigoroso) Moderado Grandes dados (Spark) Segurança de tipo, escalabilidade
Clojure Funcional (Lisp) Duro APIs centradas em dados Simplicidade, imutabilidade
Legal Dinâmico, script Fácil Compilações (Gradle) Flexibilidade, scripts