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 |
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