Pular para o conteúdo principal

Classes e Objetos

Definição de classes

Classes são modelos para criar objetos:

class Pessoa {
String nome;
int idade;

Pessoa(this.nome, this.idade);

void apresentar() {
print("Olá, meu nome é $nome e tenho $idade anos");
}
}

// Criar instância
var pessoa = Pessoa("João", 25);
pessoa.apresentar();

Members (propriedades e métodos)

class ListaDeTarefas {
// Propriedades
String usuario;
int quantidadeTarefas;

// Constructor
ListaDeTarefas(this.usuario, this.quantidadeTarefas);

// Métodos
void adicionarTarefa(int quantidade) {
quantidadeTarefas += quantidade;
}

void removerTarefa(int quantidade) {
if (quantidadeTarefas >= quantidade) {
quantidadeTarefas -= quantidade;
} else {
print("Quantidade insuficiente de tarefas");
}
}

int consultarQuantidade() {
return quantidadeTarefas;
}
}

Membros privados

Em Dart, membros privados são indicados com um underscore (_) no início do nome. Membros privados só podem ser acessados dentro da mesma biblioteca (arquivo).

class ListaDeTarefas {
String usuario; // Público - pode ser acessado de fora
int _quantidadeTarefas; // Privado - só acessível dentro desta classe/biblioteca

ListaDeTarefas(this.usuario, this._quantidadeTarefas);

// Método público
void adicionarTarefa(int quantidade) {
_quantidadeTarefas += quantidade; // Pode acessar _quantidadeTarefas dentro da classe
}

// Método privado
void _validarQuantidade(int quantidade) {
if (quantidade <= 0) {
throw ArgumentError("Quantidade deve ser positiva");
}
}

void removerTarefa(int quantidade) {
_validarQuantidade(quantidade); // Pode chamar método privado
if (_quantidadeTarefas >= quantidade) {
_quantidadeTarefas -= quantidade;
} else {
print("Quantidade insuficiente de tarefas");
}
}

// Getter público para acessar a quantidade
int get quantidadeTarefas => _quantidadeTarefas;
}

var lista = ListaDeTarefas("João", 10);
print(lista.usuario); // OK - membro público
// print(lista._quantidadeTarefas); // Erro! _quantidadeTarefas é privado
print(lista.quantidadeTarefas); // OK - usando o getter público
lista.adicionarTarefa(5);
// lista._validarQuantidade(3); // Erro! método privado

Importante:

  • Membros sem _ são públicos e podem ser acessados de qualquer lugar
  • Membros com _ são privados e só podem ser acessados dentro do mesmo arquivo
  • Use membros privados para encapsular detalhes de implementação e proteger dados sensíveis
  • Getters e setters públicos podem ser usados para controlar o acesso a membros privados

Constructors

Default constructor

class Pessoa {
String nome;
int idade;

Pessoa(this.nome, this.idade);
}

Named constructors

Construtores nomeados permitem diferentes formas de criar uma instância:

class Pessoa {
String nome;
int idade;

Pessoa(this.nome, this.idade);

Pessoa.recemNascido(String nome) : this(nome, 0);

Pessoa.anonima() : this("Anônimo", 0);
}

var bebe = Pessoa.recemNascido("Maria");
var anonimo = Pessoa.anonima();

Const constructors

Para objetos imutáveis:

class Ponto {
final int x;
final int y;

const Ponto(this.x, this.y);
}

const origem = Ponto(0, 0);

Getters e Setters

Getters e setters permitem controlar o acesso a propriedades:

class Retangulo {
double _largura;
double _altura;

Retangulo(this._largura, this._altura);

// Getter
double get area => _largura * _altura;

// Setter
set largura(double valor) {
if (valor > 0) {
_largura = valor;
}
}

double get largura => _largura;
double get altura => _altura;
}

var retangulo = Retangulo(5, 3);
print(retangulo.area); // 15
retangulo.largura = 10;
print(retangulo.area); // 30

Herança (extends)

Herança permite criar classes baseadas em outras classes:

class Animal {
String nome;

Animal(this.nome);

void fazerSom() {
print("Som genérico");
}
}

class Cachorro extends Animal {
Cachorro(String nome) : super(nome);


void fazerSom() {
print("Au au!");
}

void latir() {
print("$nome está latindo");
}
}

var cachorro = Cachorro("Rex");
cachorro.fazerSom(); // "Au au!"
cachorro.latir(); // "Rex está latindo"

Abstract classes

Classes abstratas não podem ser instanciadas diretamente:

abstract class Forma {
double calcularArea();

void desenhar() {
print("Desenhando forma");
}
}

class Circulo extends Forma {
double raio;

Circulo(this.raio);


double calcularArea() {
return 3.14159 * raio * raio;
}
}

// var forma = Forma(); // Erro! Não pode instanciar classe abstrata
var circulo = Circulo(5);
print(circulo.calcularArea()); // 78.54

Mixins

Mixins permitem reutilizar código em múltiplas hierarquias de classes:

mixin Nadador {
void nadar() {
print("Nadando...");
}
}

mixin Voador {
void voar() {
print("Voando...");
}
}

class Pato with Nadador, Voador {
String nome;

Pato(this.nome);
}

var pato = Pato("Donald");
pato.nadar(); // "Nadando..."
pato.voar(); // "Voando..."

Extension methods

Extensions permitem adicionar funcionalidades a classes existentes:

extension StringExtension on String {
String capitalize() {
if (isEmpty) return this;
return "${this[0].toUpperCase()}${substring(1)}";
}

bool isValidEmail() {
return contains('@') && contains('.');
}
}

String nome = "joão";
print(nome.capitalize()); // "João"

String email = "teste@email.com";
print(email.isValidEmail()); // true

Exercícios

  1. Criação de classes: Crie uma classe Biblioteca com propriedades codigo (String), quantidadeLivros (int) e nome (String). Adicione métodos adicionarLivro e emprestarLivro. O método emprestarLivro deve verificar se há livros disponíveis.

  2. Getters e Setters: Crie uma classe Retangulo com propriedades privadas _largura e _altura. Crie getters para acessar os valores e setters que validem se os valores são positivos. Adicione um getter area que calcule a área.

  3. Named constructors: Crie uma classe Pessoa com propriedades nome e idade. Crie um constructor padrão e named constructors: Pessoa.recemNascido (idade 0) e Pessoa.anonima (nome "Anônimo", idade 0).

  4. Herança: Crie uma classe abstrata Animal com método abstrato fazerSom(). Crie classes Cachorro e Gato que herdem de Animal e implementem o método fazerSom().

  5. Mixins: Crie mixins Nadador e Voador com métodos nadar() e voar(). Crie uma classe Pato que use ambos os mixins e demonstre o uso dos métodos.

Soluções

Ver solução do exercício 1
class Biblioteca {
String codigo;
int quantidadeLivros;
String nome;

Biblioteca(this.codigo, this.quantidadeLivros, this.nome);

void adicionarLivro(int quantidade) {
if (quantidade > 0) {
quantidadeLivros += quantidade;
print("$quantidade livro(s) adicionado(s). Total disponível: $quantidadeLivros");
} else {
print("Quantidade inválida");
}
}

void emprestarLivro(int quantidade) {
if (quantidade > 0) {
if (quantidadeLivros >= quantidade) {
quantidadeLivros -= quantidade;
print("$quantidade livro(s) emprestado(s). Livros restantes: $quantidadeLivros");
} else {
print("Livros insuficientes. Disponível: $quantidadeLivros");
}
} else {
print("Quantidade inválida");
}
}

void consultarDisponibilidade() {
print("Biblioteca $codigo ($nome): $quantidadeLivros livro(s) disponível(eis)");
}
}

void main() {
var biblioteca = Biblioteca("BIB-001", 100, "Biblioteca Central");
biblioteca.consultarDisponibilidade();
biblioteca.adicionarLivro(50);
biblioteca.emprestarLivro(30);
biblioteca.emprestarLivro(200); // Livros insuficientes
}
Ver solução do exercício 2
class Retangulo {
double _largura;
double _altura;

Retangulo(this._largura, this._altura);

double get largura => _largura;
double get altura => _altura;

set largura(double valor) {
if (valor > 0) {
_largura = valor;
} else {
print("Largura deve ser positiva");
}
}

set altura(double valor) {
if (valor > 0) {
_altura = valor;
} else {
print("Altura deve ser positiva");
}
}

double get area => _largura * _altura;
double get perimetro => 2 * (_largura + _altura);
}

void main() {
var retangulo = Retangulo(5.0, 3.0);
print("Área: ${retangulo.area}");
print("Perímetro: ${retangulo.perimetro}");

retangulo.largura = 10.0;
retangulo.altura = 4.0;
print("Nova área: ${retangulo.area}");

retangulo.largura = -5.0; // Erro: Largura deve ser positiva
}
Ver solução do exercício 3
class Pessoa {
String nome;
int idade;

Pessoa(this.nome, this.idade);

Pessoa.recemNascido(String nome) : this(nome, 0);

Pessoa.anonima() : this("Anônimo", 0);

void apresentar() {
print("Nome: $nome, Idade: $idade");
}
}

void main() {
var pessoa1 = Pessoa("João", 25);
pessoa1.apresentar();

var bebe = Pessoa.recemNascido("Maria");
bebe.apresentar();

var anonimo = Pessoa.anonima();
anonimo.apresentar();
}
Ver solução do exercício 4
abstract class Animal {
String nome;

Animal(this.nome);

void fazerSom();

void apresentar() {
print("Eu sou $nome");
}
}

class Cachorro extends Animal {
Cachorro(String nome) : super(nome);


void fazerSom() {
print("$nome faz: Au au!");
}

void latir() {
print("$nome está latindo");
}
}

class Gato extends Animal {
Gato(String nome) : super(nome);


void fazerSom() {
print("$nome faz: Miau!");
}

void miar() {
print("$nome está miando");
}
}

void main() {
var cachorro = Cachorro("Rex");
cachorro.apresentar();
cachorro.fazerSom();
cachorro.latir();

var gato = Gato("Mimi");
gato.apresentar();
gato.fazerSom();
gato.miar();
}
Ver solução do exercício 5
mixin Nadador {
void nadar() {
print("Estou nadando...");
}
}

mixin Voador {
void voar() {
print("Estou voando...");
}
}

class Pato with Nadador, Voador {
String nome;

Pato(this.nome);

void apresentar() {
print("Eu sou $nome, um pato");
}
}

class Peixe with Nadador {
String nome;

Peixe(this.nome);

void apresentar() {
print("Eu sou $nome, um peixe");
}
}

void main() {
var pato = Pato("Donald");
pato.apresentar();
pato.nadar();
pato.voar();

var peixe = Peixe("Nemo");
peixe.apresentar();
peixe.nadar();
// peixe.voar(); // Erro! Peixe não tem mixin Voador
}