DEV Community

loading...

Scala - Orientação a Objetos, Classes e Interfaces

jzo profile image João Gabriel Zó ・4 min read

Além de funcional, Scala também tem um pezinho na orientação objeto. Com isso, alguns conceitos já conhecidos pra quem vem de outras linguagens podem muito bem ser aplicados em Scala.

Classes Abstratas

São classes que podem conter membros cujo a implementação não está definida. Por esse motivo, uma instância de uma Classe Abstrata não pode ser criada com o operador new.

As classes abstratas são sempre herdadas por uma subclasse, a qual vai implementar seus métodos.

abstract class IntSet {

    def contains(x: Int): Boolean
    def incl(x: Int): IntSet
    def union(other: IntSet): IntSet
}

Vamos implementar a classe abstrata IntSet como uma árvore binária persistente com 2 tipos possíveis:

  1. Uma árvore para IntSet vazio
  2. Uma árvore para IntSet contendo 1 número e 2 sub-árvores

No nosso exemplo, as sub-árvores da direita serão sempre maiores que as da esquerda.

Estruturas de Dados Persistentes são tipos de estruturas que sempre perservam sua versão anterior quando é modificada. São efetivamente imutáveis, pois suas operações não atualizam a estrutura no local, mas sempre produzem uma nova, atualizada.
Estruturas de dados persistentes são um dos pilares da programação funcional.

class Empty extends IntSet {

    // Como esse é um IntSet vazio, ele nunca vai conter um elemento
    def contains(x: Int): Boolean = false
    // Mesmo sendo um IntSet vazio, podem ser adicionados novos "ramos".
    def incl(x: Int): IntSet = new NonEmpty(x, new Empty, new Empty)

    // Esse método retorna uma representação do objeto em forma de String.
    override def toString: String = "."
}

class NonEmpty(elem: Int, left: IntSet, right: IntSet) extends IntSet {

    def contains(x: Int): Boolean = {
        /*
            Esse método vai procurar o valor "x" no IntSet. 
            Se for menor que o principal do IntSet, vai procurar no elemento à esquerda. 
            Se for maior, no elemento à direita.
            Se for igual ao elemento em questão, retornará true.
        */
        if (x < elem) left contains x
        else if (x > elem) right contains x
        else true
    }

    def incl(x: Int): IntSet = {
        /*
            Esse método vai incluir o valor "x" no IntSet. 
            A lógica é a mesma do método anterior, porém se o valor for 
            igual ao elemento do IntSet, irá retornar o próprio IntSet,
            pois não pode conter elementos repetidos.
        */
        if (x < elem) new NonEmpty(elem, left incl x, right)
        else if (x > elem) new NonEmpty(elem, left, right incl x)
        else this
    }

    override def toString: String = s"{$left$elem$right}"
}

Classes

As definições de contains e incl nas classes Empty e NonEmpty implementam as funções abstratas na classe base IntSet.
Também é possível redefinir uma função não abstrata, como foi feito com a função toString, utilizando override antes de declarar a mesma.

Se as classes Empty e NonEmpty herdam de IntSet, isso implica que ambas se conformam à classe IntSet. O que isso quer dizer, de fato, é que tanto Empty quanto NonEmpty podem ser usadas onde IntSet é requirido.

Em Scala, toda classe definida pelo usuário herda de alguma outra classe. Se nenhuma super classe é passada, a classe Object, do pacote java.lang é adotada.
As super classes, diretas ou indiretas, de uma classe X são chamadas Classes Base dessa classe X. Logo, as classes base de NonEmpty são IntSet e Object.

Objects

Diferente da classe java.lang.Object, esse Object é um tipo de mescla e abreviação para se definir uma classe de uso único, a qual não pode ser instanciada. Elas também podem ser usadas como Companion Objects, que eu pretendo falar no futuro.

Para que Empty não precise ser instanciada tantas vezes, a classe pode ser melhor expressada como um object.

object Empty extends IntSet {

    def contains(x: Int): Boolean = false
    def incl(x: Int): IntSet = new NonEmpty(x, Empty, Empty)
    def union(other: IntSet): IntSet = other

    override def toString: String = "."
}

Traits

São exatamente iguais às classes abstratas em sua estrutura, tendo como única diferença a possibilidade de ser herdada com with. Isso quer dizer que você pode herdar apenas 1 classe abstrata (extends C), e quantas traits quiser (with T with T2 with T3).
Traits são parecidas com as Interfaces em java, mas mais poderosas, pois podem conter campos e métodos concretos. Por outro lado, traits não podem conter variáves val.

Tipos

No topo da hierarquia de tipos estão:

  • Any - É a base de todos os tipos existentes.
  • AnyRef - É o tipo base de todos os tipos referência (tipos que não são valores). É um pseudônimo da classe java.lang.Object.
  • AnyVal - É o tipo base de todos os tipos primitivos.


 

Nothing e Null

Nothing está no fundo da hierarquia de tipos de Scala. Ele é um subtipo de todos os outros tipos, e não existe um valor do tipo Nothing.
Ele é útil para sinalizar terminações anormais e também como o tipo de elemento para uma collection vazia

val lista: List[Nothing]

A forma como Scala lida com exceções é parecida com Java. A expressão throw new Exception aborta o programa com a exceção Exception.
O tipo dessa expressão é Nothing.

Null é um subtipo de toda classe do tipo referência, logo, pertence a AnyRef.
Por ser um subtipo das classes que herdam de java.lang.Object, Null é incompatível com qualquer subtipo de AnyVal.

val x: Null = null
val y: Integer = null
val z: Int = null // error: type mismatch




Polimorfismo

Polimorfismo significa que o tipo de uma função pode vir de muitas formas. É uma forma de tornar a linguagem mais expressiva, enquanto continua mantendo toda sua tipagem estática segura.
Usando o polimorfismo, a função ou tipo de dado pode ser escrita genericamente para que possa suportar valores idênticos sem depender de seu tipo.
Existem 2 formas principais do polimorfismo:

  • Subtyping - As instâncias de uma subclasse podem ser passadas para uma classe base
  • Generics - As instâncias de uma função ou classe são criadas pela parametrização do tipo.  

Discussion (0)

pic
Editor guide