Java não é POO pura
E aí, galera.
Hoje vamos bater um papo sobre um assunto que sempre gera treta entre dev: Java não é POO.
Mesmo que todo mundo fale que Java é a linguagem orientada a objetos por excelência, ele não é POO pura. Ele é quase, mas tem uns furos que quebram a filosofia real de orientação a objetos. Vamos direto nos pontos que mais doem porque são os que mostram o problema na cara.
Entendendo o problema com POO no Java
POO de verdade, como em Smalltalk ou Ruby, trata tudo como objeto. Tudo mesmo. O número cinco é um objeto. O booleano true é um objeto. Até o nada tem comportamento. Você manda mensagem para eles e eles respondem. No Java isso não rola de forma limpa. O compilador até tenta disfarçar, mas a verdade aparece rápido.
Resultado: você acha que está fazendo POO mas, na real, está misturando coisas. Vamos ver na prática.
Os tipos primitivos são os pobres da festa
Em POO pura, tudo é objeto.
Quer chamar um método no número cinco? Pode.
Em Ruby você faz 5.next.
Em Smalltalk você faz 5 succ.
Porque o cinco não é só um valor. Ele é uma instância de uma classe que tem estado e comportamento.
No Java o negócio muda.
int x = 5;
Você não consegue fazer x.proximo() nem chamar nenhum método direto. Ele é só um pedacinho de memória com um número dentro. Não tem classe, não tem métodos, não recebe mensagem. É um cidadão de segunda classe.
Para consertar isso, o Java criou os wrappers: Integer, Double, Boolean e por aí vai. Aí você faz:
Integer y = 5;
E agora sim vira objeto. Mas olha o absurdo: você precisa ficar convertendo para frente e para trás com autoboxing e unboxing, o compilador faz mágica para esconder e, na real, o int continua sendo primitivo. É tipo colocar terno num mendigo para fingir que ele é burguês.
Resumo: em POO pura o número cinco é um objeto que sabe se comportar. No Java, o número cinco é só um número. FIM.
Vale mencionar: o Project Valhalla, em desenvolvimento no OpenJDK, vai introduzir value types — tipos que se comportam como objetos mas sem o overhead de alocação no heap. É a tentativa mais séria do Java de fechar esse buraco. Mas enquanto não chega, primitivos continuam sendo primitivos.
Método static é programação procedural com fantasia de classe
Sabe aquelas classes cheias de public static que a gente vê por todo lugar? Tipo isso aqui:
public class Utils {
public static String formatarNome(String nome) { ... }
public static double calcularImposto(double valor) { ... }
public static boolean ehMaiorDeIdade(int idade) { ... }
}
Isso não é POO, irmão. Isso é C disfarçado de Java.
Em POO de verdade você cria objetos e manda mensagens entre eles:
pessoa.formatarNome();
nota.calcularImposto();
cliente.ehMaiorDeIdade();
Cada objeto tem seu estado e seu comportamento. O objeto é o protagonista.
Quando você joga tudo em static, está fazendo exatamente o que a programação estruturada fazia nos anos setenta: funções globais soltas, só que agora embrulhadas dentro de uma classe para fingir que é orientado a objetos. É tipo colocar terno no cachorro e falar que ele é CEO.
O pior é que a galera faz isso o tempo todo. DateUtils, StringUtils, ControllerUtils e ainda acha que está fazendo POO. Não está. Está fazendo procedural com taxa de encapsulamento.
Vale mencionar: o Java 8 trouxe lambdas e interfaces funcionais que suavizam esse problema. Em vez de uma classe de utils estática, você pode passar comportamento como objeto usando
Function,Predicate,Consumer. Ainda não é POO pura, mas é menos procedural do que um monte destatic.
Outros furos que fecham o caso
Arrays não são objetos de verdade
Arrays no Java têm uma sintaxe própria que não segue o modelo de objetos:
int[] numeros = {1, 2, 3};
int tamanho = numeros.length; // campo público, não método
Em POO pura, você chamaria um método: numeros.tamanho(). No Java, length é um campo especial, não um método. Você não consegue herdar de um array, ele não implementa as interfaces de Collection e não tem acesso ao código-fonte dessa "classe". É um tipo fantasma que vive fora do sistema de objetos normal.
Compare com uma lista, que é cidadã de primeira classe:
List<Integer> lista = List.of(1, 2, 3);
int tamanho = lista.size(); // método de verdade, com herança e polimorfismo
Type erasure apaga a identidade dos genéricos
Quando você escreve List<String>, parece que criou um objeto com identidade própria. Mas em runtime o Java apaga essa informação:
List<String> nomes = new ArrayList<>();
List<Integer> numeros = new ArrayList<>();
System.out.println(nomes.getClass() == numeros.getClass()); // true
Os dois são a mesma coisa em tempo de execução. O objeto não sabe qual tipo carrega. Isso quebra o princípio de que um objeto tem identidade e estado bem definidos. É POO com amnésia.
Herança simples deixa o modelo incompleto
Java só permite herdar de uma classe. Em linguagens de POO mais completas, como Eiffel, você tem múltipla herança. Em Ruby e Scala, você tem mixins e traits que permitem compor comportamento de forma flexível.
O Java tentou resolver isso com interfaces e, a partir do Java 8, com default methods. É uma solução razoável, mas ainda não é o mesmo que compor objetos livremente como nas linguagens puristas.
O Java está tentando melhorar
Para ser justo, o Java não está parado. Nos últimos anos chegaram evoluções que fecham parte desses buracos:
- Records (Java 16): classes de dados imutáveis sem boilerplate. Mais próximas de um objeto coeso.
- Sealed classes (Java 17): controle explícito de hierarquia. Mais rigor no modelo de objetos.
- Pattern matching (Java 21): reduz a necessidade de casts explícitos, deixando o código mais orientado ao comportamento.
- Project Valhalla (em desenvolvimento): vai trazer value types, a correção mais profunda para o problema dos primitivos.
São passos na direção certa. Mas não mudam o fato de que, hoje, Java não é POO pura.
Conclusão
Java é orientado a objetos o suficiente para te fazer criar classes, herança, polimorfismo e tal. Mas ele não é puro. Ele tem tipos que não são objetos, funções que não precisam de objeto, primitivos que vivem fora do mundo orientado a objetos.
Por isso os puristas olham para o Java e falam: "bonitinho, mas não é POO de verdade." É como se o Java fosse o cara que vai na festa de black tie de jeans e tênis dizendo "mas eu estou elegante, sim, olha o cinto de marca." Elegante é. POO pura? Nem fodendo.
Você já refatorou uma classe de Utils para um modelo mais orientado a objetos? Como ficou a estrutura? Me conta nos comentários.
Vamos trocar ideia.
Até a próxima.