Projeto de estudo com exemplos práticos, comentados e executáveis dos
conceitos fundamentais de Programação Orientada a Objetos (POO) e dos
princípios SOLID. Cada conceito vive em seu próprio arquivo, com uma função
main() que demonstra o comportamento no console.
💡 Todos os comentários do código estão em português, pensados para quem está aprendendo. Leia o código junto com este README.
src/main/kotlin/
├── fundamentos/
│ ├── 01_ClassesEObjetos.kt → Classe x Objeto
│ ├── 02_Encapsulamento.kt → Encapsulamento
│ ├── 03_Heranca.kt → Herança
│ ├── 04_Polimorfismo.kt → Polimorfismo
│ ├── 05_Abstracao.kt → Abstração (classe abstrata)
│ ├── 06_Interface.kt → Interfaces
│ └── 07_Composicao.kt → Composição ("TEM UM")
└── solid/
├── 01_SRP_SingleResponsibility.kt
├── 02_OCP_OpenClosed.kt
├── 03_LSP_LiskovSubstitution.kt
├── 04_ISP_InterfaceSegregation.kt
└── 05_DIP_DependencyInversion.kt
Cada arquivo tem sua própria função main(). A forma mais simples de rodar é
pela IntelliJ IDEA: abra o arquivo e clique na fun main().
Pela linha de comando você pode compilar tudo com:
./gradlew compileKotlinO projeto usa Kotlin 2.3.21 e a toolchain JDK 25 (configurada em
build.gradle.kts). O Gradle baixa a JDK automaticamente na primeira execução.
Os 4 pilares clássicos são Abstração, Encapsulamento, Herança e Polimorfismo. Junto deles, Interface e Composição completam o kit essencial de design. Abaixo, cada um com seu exemplo.
1. Classe e Objeto — 01_ClassesEObjetos.kt
| Conceito | O que é |
|---|---|
| Classe | O molde / a planta. Define os atributos (estado) e comportamentos (métodos). |
| Objeto | A instância criada a partir da classe. Cada um tem seu próprio estado. |
Classe do exemplo:
Carro— possui os atributosmarca,modeloevelocidade, e os métodosacelerar(),frear()edescricao(). Nomain()criamos dois objetos (fuscaecivic) e vemos que cada um guarda seu estado de forma independente.
2. Encapsulamento — 02_Encapsulamento.kt
Esconder os detalhes internos e proteger o estado do objeto, expondo apenas uma interface segura. Assim ninguém coloca o objeto em um estado inválido.
Classe do exemplo:
ContaBancaria— osaldoéprivate set(lê-se de fora, mas só muda por dentro). As únicas portas de entrada sãodepositar()esacar(), ambas com validação (require, checagem de saldo). Tentarconta.saldo = 9999.0nem compila — é exatamente essa a proteção do encapsulamento.
🔑 Modificadores de visibilidade: public (padrão), private, protected, internal.
3. Herança — 03_Heranca.kt
Criar uma classe nova reaproveitando atributos e comportamentos de outra,
numa relação "É UM". Em Kotlin é preciso marcar a classe/método como open
para permitir herança e usar override para especializar.
Classes do exemplo:
Animal(superclasseopen) — temnome, o métododormir()(comum a todos) eemitirSom()(marcadoopenpara ser sobrescrito).Cachorro— herda deAnimal, fazoverridedeemitirSom()("Au au!") e adiciona o método próprioabanarRabo().Gato— herda deAnimale usasuper.emitirSom()para reaproveitar a implementação do pai antes de adicionar o "Miau!".
4. Polimorfismo — 04_Polimorfismo.kt
"Muitas formas": a mesma chamada se comporta de maneiras diferentes conforme o objeto real por trás dela.
Classes do exemplo:
Funcionario(base),Gerente(+50% de bônus) eVendedor(base + comissão) — todos têmcalcularSalario(). Ao percorrer umaList<Funcionario>, a mesma chamadaf.calcularSalario()produz resultados diferentes → polimorfismo de sobrescrita (em tempo de execução).Calculadora— três métodossomar()com parâmetros diferentes → polimorfismo de sobrecarga (overload, em tempo de compilação).
5. Abstração — 05_Abstracao.kt
Focar no O QUE um objeto faz, escondendo o COMO. Uma classe abstrata não pode ser instanciada e pode obrigar as subclasses a implementarem certos métodos.
Classes do exemplo:
FormaGeometrica(abstrata) — define os métodos abstratosarea()eperimetro()(sem corpo) e o método concretodescrever()(reaproveitado por todas).Circulo,Retangulo,TrianguloEquilatero— cada uma implementaarea()/perimetro()com sua própria fórmula. O código que chamadescrever()não precisa saber qual forma é.
Abstração vs. Interface: a classe abstrata pode guardar estado e expressa "É UM"; a interface expressa uma capacidade ("consigo fazer X") e permite implementação múltipla.
6. Interface — 06_Interface.kt
Um contrato de capacidades. Uma classe pode implementar várias interfaces (diferente da herança, que permite só uma superclasse).
Tipos do exemplo:
Voador— contrato comvoar()e um método defaultaterrissar().Nadador— contrato comnadar().Pato— implementa as duas (voa e nada).Pinguim— implementa sóNadador(não voa).Aviao— implementa sóVoador(mesmo não sendo um animal — a interface conecta tipos sem relação de herança).
7. Composição — 07_Composicao.kt
Montar um objeto complexo a partir de outros, numa relação "TEM UM". Princípio de ouro: "prefira composição à herança" — é mais flexível e tem menor acoplamento.
Classes do exemplo:
CPU,MemoriaRAM,ArmazenamentoSSD— peças pequenas, cada uma com responsabilidade única.Computador— é composto por essas peças (recebidas no construtor) e delega o trabalho a elas emligar(). Trocamos um "PC básico" por um "PC gamer" só mudando as peças, sem criar novas classes via herança.
SOLID são 5 princípios para escrever código orientado a objetos mais limpo,
flexível e fácil de manter. Em cada arquivo você encontra a versão ❌ errada
(com sufixo Ruim) e a ✅ correta, lado a lado, para comparar.
S — Responsabilidade Única (SRP) — 01_SRP_SingleResponsibility.kt
"Uma classe deve ter apenas um motivo para mudar."
- ❌
RelatorioRuim— calcula, formata em texto e salva em arquivo (três responsabilidades = três motivos para mudar). - ✅ Separadas em
Relatorio(dados/regra),FormatadorRelatorio(formatação) eRepositorioRelatorio(persistência). Cada uma muda e é testada de forma independente.
O — Aberto/Fechado (OCP) — 02_OCP_OpenClosed.kt
"Aberto para extensão, fechado para modificação."
- ❌
CalculadoraDescontoRuim— umwhenpor tipo de cliente; cada novo tipo exige editar a classe. - ✅ Contrato
RegraDescontoimplementado porClienteComum,ClientePremium,ClienteVipeClienteParceiro. Um novo desconto = uma nova classe, sem tocar no código existente. ACalculadoraDescontosó conhece a abstração.
L — Substituição de Liskov (LSP) — 03_LSP_LiskovSubstitution.kt
"Um subtipo deve poder substituir o tipo base sem quebrar o programa."
- ❌
AveRuimassume que toda ave voa;PinguimRuimé forçado a lançar exceção emvoar()→ substituir a base por ele quebra o programa. - ✅
Avesó define o que toda ave faz (comer()); a capacidade de voar fica na interfacePodeVoar, implementada porPardaleAguia.Pinguimé umaAveque simplesmente não implementaPodeVoar— sem mentiras, sem exceções.
I — Segregação de Interfaces (ISP) — 04_ISP_InterfaceSegregation.kt
"Ninguém deve ser forçado a depender de métodos que não usa."
- ❌
TrabalhadorRuimé uma interface "gorda" (trabalhar+comer+dormir).RoboRuimé obrigado a implementarcomer()/dormir()que não fazem sentido para ele. - ✅ Interfaces pequenas e coesas:
Trabalhavel,Alimentavel,Descansavel.Humanoimplementa as três;Roboimplementa sóTrabalhavel.
D — Inversão de Dependência (DIP) — 05_DIP_DependencyInversion.kt
"Dependa de abstrações, não de implementações concretas."
- ❌
NotificadorRuiminstancia e depende diretamente deEmailService. Trocar o canal exige editar o notificador. - ✅ A abstração
CanalDeMensagemé implementada porEmailSender,SmsSendereWhatsappSender. A classeNotificadorrecebe o canal pelo construtor (injeção de dependência) e funciona com qualquer um — inclusive um fake em testes — sem nunca ser modificada.
| # | Conceito | Ideia em uma frase | Arquivo |
|---|---|---|---|
| 1 | Classe/Objeto | Molde × instância | fundamentos/01_ClassesEObjetos.kt |
| 2 | Encapsulamento | Proteger o estado interno | fundamentos/02_Encapsulamento.kt |
| 3 | Herança | Reaproveitar ("É UM") | fundamentos/03_Heranca.kt |
| 4 | Polimorfismo | Mesma chamada, comportamentos diferentes | fundamentos/04_Polimorfismo.kt |
| 5 | Abstração | O QUE, não o COMO | fundamentos/05_Abstracao.kt |
| 6 | Interface | Contrato de capacidades | fundamentos/06_Interface.kt |
| 7 | Composição | Montar com peças ("TEM UM") | fundamentos/07_Composicao.kt |
| S | SRP | Um motivo para mudar | solid/01_SRP_SingleResponsibility.kt |
| O | OCP | Estender sem modificar | solid/02_OCP_OpenClosed.kt |
| L | LSP | Subtipo substitui a base | solid/03_LSP_LiskovSubstitution.kt |
| I | ISP | Interfaces pequenas | solid/04_ISP_InterfaceSegregation.kt |
| D | DIP | Depender de abstrações | solid/05_DIP_DependencyInversion.kt |
Bons estudos! 🚀 Leia o código, rode os main() e experimente modificar os
exemplos para fixar cada conceito.