# Regla: Estilo de Código Java (Java 17+)

Esta regla es OBLIGATORIA para todo código Java nuevo o modificado.
Java moderno (17+) ofrece construcciones que eliminan verbosidad sin sacrificar
claridad. Usarlas no es opcional: reducen bugs y aumentan legibilidad.

---

## Formateador obligatorio

- Usar **google-java-format** o **Spotless** con perfil AOSP (4 espacios).
  Alternativa aceptable: Checkstyle con configuración de Google Style Guide.
- El formateador corre en CI como verificación bloqueante. Un PR que no formatea
  no pasa la pipeline sin importar el resto.
- Configurar el IDE para formatear al guardar. No formatear manualmente.

```xml
<!-- pom.xml — Spotless obligatorio -->
<plugin>
  <groupId>com.diffplug.spotless</groupId>
  <artifactId>spotless-maven-plugin</artifactId>
  <version>2.43.0</version>
  <configuration>
    <java>
      <googleJavaFormat>
        <version>1.19.2</version>
        <style>AOSP</style>
      </googleJavaFormat>
    </java>
  </configuration>
</plugin>
```

---

## Records para DTOs inmutables (Java 16+)

Los records eliminan el boilerplate de DTOs. Todo DTO inmutable DEBE ser record.

```java
// MAL — clase tradicional con boilerplate innecesario
public class UsuarioDto {
    private final String nombre;
    private final String email;
    public UsuarioDto(String nombre, String email) { ... }
    public String getNombre() { return nombre; }
    // ... equals, hashCode, toString
}

// BIEN — record: inmutable, compacto, con equals/hashCode/toString automáticos
public record UsuarioDto(String nombre, String email) {}
```

---

## Sealed classes para jerarquías cerradas (Java 17+)

Cuando una jerarquía tiene un número fijo conocido de subtipos, usar `sealed`.
Permite al compilador verificar exhaustividad en switch expressions.

```java
// BIEN
public sealed interface ResultadoPago
    permits PagoAprobado, PagoRechazado, PagoPendiente {}

public record PagoAprobado(String transaccionId) implements ResultadoPago {}
public record PagoRechazado(String motivo) implements ResultadoPago {}
public record PagoPendiente(Instant expiracion) implements ResultadoPago {}

// Switch exhaustivo — el compilador verifica que todos los casos están cubiertos
String mensaje = switch (resultado) {
    case PagoAprobado a -> "Aprobado: " + a.transaccionId();
    case PagoRechazado r -> "Rechazado: " + r.motivo();
    case PagoPendiente p -> "Pendiente hasta " + p.expiracion();
};
```

---

## Pattern matching para instanceof (Java 16+)

```java
// MAL — cast manual redundante
if (objeto instanceof Factura) {
    Factura factura = (Factura) objeto;
    procesar(factura);
}

// BIEN — pattern matching elimina el cast
if (objeto instanceof Factura factura) {
    procesar(factura);
}
```

---

## Text blocks para strings multilínea (Java 15+)

```java
// MAL — concatenación ilegible
String sql = "SELECT f.id, f.total\n" +
             "FROM facturas f\n" +
             "WHERE f.estatus = 'ACTIVA'\n" +
             "ORDER BY f.fecha_emision DESC";

// BIEN — text block preserva indentación y legibilidad
String sql = """
    SELECT f.id, f.total
    FROM facturas f
    WHERE f.estatus = 'ACTIVA'
    ORDER BY f.fecha_emision DESC
    """;
```

---

## Optional: uso correcto

- `Optional` es para valores de RETORNO que pueden estar ausentes. Solo ahí.
- NUNCA como parámetro de método — usar sobrecarga o valor por defecto.
- NUNCA como campo de clase — usar null con documentación o valor centinela.

```java
// MAL — Optional como parámetro
public void enviarCorreo(String destinatario, Optional<String> asunto) { ... }

// MAL — Optional como campo
public class Pedido {
    private Optional<String> notas;  // serialización rota, null es suficiente
}

// BIEN — Optional solo en retorno
public Optional<Usuario> buscarPorEmail(String email) {
    return repositorio.findByEmail(email);
}

// BIEN — consumir con métodos funcionales, no con isPresent()/get()
buscarPorEmail(email)
    .map(Usuario::getNombre)
    .orElse("Anónimo");
```

---

## var para variables locales con tipo obvio

```java
// MAL — tipo redundante cuando el constructor lo hace obvio
HashMap<String, List<Factura>> facturasPorCliente = new HashMap<String, List<Factura>>();

// BIEN — var elimina redundancia sin perder claridad
var facturasPorCliente = new HashMap<String, List<Factura>>();

// MAL — var donde el tipo no es obvio (reduce legibilidad)
var resultado = procesarPedido(id);  // ¿Qué tipo retorna procesarPedido?

// BIEN — tipo explícito cuando no es obvio del lado derecho
ResultadoProcesamiento resultado = procesarPedido(id);
```

---

## Campos final y nombres

- Declarar `final` todo campo que no cambia después de la construcción.
- Declarar `final` todo parámetro de método cuando el método es largo.
- Clases: `PascalCase`. Métodos y variables: `camelCase`.
- Constantes: `SCREAMING_CASE` con `static final`.
- Sin abreviaciones salvo las universalmente conocidas (`id`, `dto`, `url`).

```java
// MAL
private String n;                     // nombre críptico
private int contadorTemporalLogs;     // hungarian notation innecesaria
static final int maxReintentos = 3;   // constante en camelCase

// BIEN
private final String nombre;
private int contadorLogs;
static final int MAX_REINTENTOS = 3;
```

---

## Límites de tamaño

- Máximo **40 líneas** por método (sin contar líneas en blanco y comentarios).
- Máximo **300 líneas** por clase. Si crece más: extraer clases colaboradoras.
- Máximo **5 parámetros** por método. Si se necesitan más: crear un objeto de parámetros.

---

## Organización de imports

Orden estricto, separados por línea en blanco:
1. `java.*`
2. `javax.*`
3. `jakarta.*`
4. `org.*`
5. `com.*` (librerías externas)
6. Paquetes del proyecto propio

Sin imports con wildcard (`import java.util.*`). Un tipo por línea.

---

## Checklist de estilo Java antes de abrir PR

- [ ] google-java-format / Spotless aplicado — el CI no fallará por formato
- [ ] DTOs inmutables convertidos a records
- [ ] Jerarquías cerradas usan sealed interfaces
- [ ] instanceof con pattern matching (sin cast manual)
- [ ] Optional solo en valores de retorno
- [ ] Constantes en SCREAMING_CASE con static final
- [ ] Sin imports wildcard
- [ ] Métodos <= 40 líneas, clases <= 300 líneas
- [ ] Campos inmutables marcados como final
