🇮🇪 Read in English

Classes em Scala: métodos e definições de atributos

Esse artigo também pode ser chamado “Minha saga aprendendo Scala - Parte 3” e é a continuação do artigo Minha saga aprendendo Scala - Parte 1 e Parte 2.

Como eu comentei na parte 1 dessa série, Scala é uma linguagem de programação orientada a objetos. Da Wikipedia (livre tradução)

A programação orientada a objetos (OOP) é um paradigma de programação baseado no conceito de “objetos”, que podem conter dados, na forma de campos (muitas vezes conhecidos como atributos ou propriedades), e código, na forma de procedimentos (muitas vezes conhecidos como métodos).

Neste artigo eu vou falar do que eu aprendi sobre classes de Scala, que segue o conceito de programação orientada a objetos que eu tirei da Wikipedia.

Criando uma classe

Para criar uma classe, você só precisa passar a classe de palavra-chave e o nome da classe:

class Ponto    

val ponto = new Ponto

Os parênteses são chamados de construtor de classe e você pode adicionar os atributos que sua classe pode receber, por exemplo:

class Ponto(x: Int, y: Int) 

val ponto = new Ponto(0, 0)

A parte legal começa agora! Você pode reescrever o construtor para receber diferentes atributos, podendo receber mais ou menos atributos

class Pessoa(nome: String, idade: Int) {  
    def this(nome: String) = this(nome, 0)  
    def this() = this("João", 0 )
}

val marco = new Pessoa("Marco", 30)
val bebeCarlos = new Pessoa("Carlos")
val joao = new Pesspa

Adicionando atributos na classe

No exemplo acima, esses atributos que eu adicionei à classe Pessoa não podem ser acessados diretament como marco.idade. Na realidade você tem 3 opções para definir os atributos quando está criando o construtor de uma classe. Não passando nada (como o exemplo acima), com um val or um var.

Usando nada, os atributos são privados e não podem ser acessados:

class Ponto(x: Int)

val ponto = new Ponto(1)
ponto.x // erro!

Ao usar val, você pode acessar o atributo (você cria um accessor), mas não pode alterar o valor (não terá um mutator).

class Ponto(val x: Int)

val ponto = new Ponto(1)

ponto.x      // 1
ponto.x = 2  // won't work

Então adicionar um val na frente do atributo é a mesma coisa que fazer isso:

class Ponto(xc: Int) {    
    def x = xc      // mesma coisa que   class Ponto(val x: Int)
}

Agora, se você usar var, você provavelmente já adivinhou: você terá um modificador e um acessador!

class Ponto(var x: Int)

val ponto = new Ponto(1)
println(ponto.x) // 1

ponto.x = 2 
println(ponto.x) // 2

Então você tem:

Definição de atributo Acessador? (point.x) Modificador? (point.x = 4) Pode sobrescrever??
val sim não sim
var sim sim não
nada não não sim

Usando classes

Ao definir uma classe, você pode usar outras palavras-chave extras para definir como você gostaria que sua classe se comportasse. Até agora, aprendi três palavras-chave principais:

Palavras-chave Uso permitido
class Ponto Normal class that allows creating multiple instances
final class Ponto Doesn’t allow class to be extended
sealed class Ponto Allow class to be extended but only in the same file
| Palavras-chave | Uso Permitido | | ——————– | —————————————————- | | class Ponto | Uma classe normal que permite a criação de múltiplas instâncias| | final class Ponto | A classe não pode ser extendida | | sealed class Ponto | Permite que a classe seja estendida, mas apenas no mesmo arquiv |

Métodos de classes

Como as classes possuem palavras-chave que nos permitem alterar a forma como são utilizadas, é razoável supor que também existam palavras-chave que podem alterar o comportamento delas. Criei uma pequena tabela com as propriedades de cada um e exemplos para nos ajudar a entender o que muda exatamente.

Obs: vou chamar de classe extendida a classe que herda (ou extende) da classe que estamos referenciado. Por exemplo, em class Pessoa extends Humano, a classe que estamos nos referindo é Humano enquanto que a classe extendida é Pessoa.

Classe extendidas pode usar? Classes extendidas podem sobrescrever? Instâncias das classes extendidas podem usar?
1 def / val Sim Sim Sim
2 private def / private val Não Não precisam Não
3 protected def/ protected val Sim Sim Não
4 final def / final val Sim Não Sim

Caso 1: def e val

class Pessoa {  
    val atributo = 1  
    def metodo = s"caso $atributo"
}

class Leticia extends Pessoa

val leticia = new Leticia
println(leticia.atributo)       // 1
println(leticia.metodo)         // caso 1

Caso 2: private def e private val

class Pessoa {  
    private val atributoPrivado = 2  
    private def metodoPrivado = s"caso $atributoPrivado"
}

class Leticia extends Person

val leticia = new Leticia
println(leticia.atributoPrivado)  // erro
println(leticia.metodoPrivado)    // erro

Caso 3: protected def e protected val

class Pessoa {     
    protected val atributoProtegido = 3     
    protected def metodoProtegigo = s"caso $atributoProtegido" 
}  

class Gabi extends Pessoa 

val gabi = new Gabi 
println(gabi.metodoProtegigo)      //erro

class Leticia extends Pessoa {    
    override val atributoProtegido = super.atributoProtegido    
    override def metodoProtegigo = super.metodoProtegigo 
} 

val leticia = new Leticia 
println(leticia.metodoProtegigo)   // caso 3 

Caso 4: final def e final val

class Pessoa {  
    final val atributoFinal = 4  
    final def metodoFinal = s"caso $atributoFinal"
}

class Leticia extends Pessoa {  
    override val atributoFinal = super.atributoFinal // erro
}

class Marco extends Pessoa

val marco = new Marco
println(marco.metodoFinal)      // caso 4

Já que eu queria que este post fosse mais um cola do que um texto pesado … é isso por enquanto 🙂 Vejo você na Parte 4 (talvez?)!


Abraço!
Letícia

Comments