Manual de Programación Java — Diagramas de Clases UML
Guía Completa para Modelado Orientado a Objetos
Versión: Java 17+ | UML 2.5 | Nivel: Principiante a Intermedio
Tabla de Contenidos
- Manual de Programación Java — Diagramas de Clases UML
- Tabla de Contenidos
- 1. Introducción al Diagrama de Clases UML
- 2. Estructura de una Clase en UML
- 3. Herencia — extends
- 4. Interfaces y Clases Abstractas
- 5. Relaciones entre Clases
- 6. Patrones de Diseño en UML
- 7. Generics y Colecciones en UML
- 8. Caso de Estudio Completo — Sistema Biblioteca
- 9. Referencia Rápida UML → Java
- 10. Buenas Prácticas y Errores Comunes
- Apéndice — Símbolos UML de Referencia
1. Introducción al Diagrama de Clases UML
UML (Unified Modeling Language) es un lenguaje estándar de modelado para sistemas orientados a objetos. El Diagrama de Clases es el diagrama más fundamental y sirve como plano estructural de cualquier sistema de software.
1.1 ¿Qué es un Diagrama de Clases?
Un Diagrama de Clases representa la estructura estática de un sistema, mostrando sus clases, atributos, métodos y las relaciones entre ellos. Es la base para escribir código Java orientado a objetos.
💡 Regla de Oro: Un Diagrama de Clases bien construido se traduce directamente a código Java. Cada caja representa una clase, cada línea representa código de relación.
1.2 Componentes Fundamentales
| Elemento | Descripción |
|---|---|
| Clase | Rectángulo dividido en 3 secciones: nombre, atributos y métodos |
| Atributo | Variable de instancia con visibilidad, nombre y tipo |
| Método | Operación con visibilidad, nombre, parámetros y tipo de retorno |
| Relación | Línea o flecha que conecta dos clases con semántica específica |
| Multiplicidad | Cardinalidad de la relación: 1, 0..*, 1..*, etc. |
2. Estructura de una Clase en UML
2.1 Sintaxis de Atributos y Métodos
La notación UML para miembros de una clase sigue estas reglas:
| Notación | Significado |
|---|---|
visibilidad nombre: Tipo | Formato general de atributo |
visibilidad nombre(params): ReturnType | Formato general de método |
+ | public — accesible desde cualquier clase |
- | private — accesible solo dentro de la clase |
# | protected — accesible en la clase y sus subclases |
~ | package — accesible dentro del mismo paquete |
{abstract} | El método no tiene implementación |
{static} | Miembro pertenece a la clase, no a instancias (subrayado en UML) |
2.2 Representación de una Clase
┌─────────────────────────┐
│ Persona │ ← Nombre de la clase
├─────────────────────────┤
│ - nombre: String │ ← Atributos (privados)
│ - edad: int │
│ - correo: String │
├─────────────────────────┤
│ + getNombre(): String │ ← Métodos (públicos)
│ + setNombre(s: String) │
│ + getEdad(): int │
│ + toString(): String │
└─────────────────────────┘
2.3 Código Java Equivalente
public class Persona {
// Atributos privados (- en UML)
private String nombre;
private int edad;
private String correo;
// Constructor
public Persona(String nombre, int edad, String correo) {
this.nombre = nombre;
this.edad = edad;
this.correo = correo;
}
// Métodos públicos (+ en UML)
public String getNombre() { return nombre; }
public void setNombre(String s) { this.nombre = s; }
public int getEdad() { return edad; }
@Override
public String toString() {
return "Persona{nombre='" + nombre + "', edad=" + edad + "}";
}
}
3. Herencia — extends
La herencia permite que una clase (subclase) adquiera propiedades y comportamientos de otra (superclase). En UML se representa con una flecha de punta triangular hueca apuntando hacia la superclase.
3.1 Notación UML
┌───────────────────┐
│ Animal │
├───────────────────┤
│ - nombre: String │
│ - edad: int │
├───────────────────┤
│ + getNombre() │
│ + hacerSonido() │
└────────┬──────────┘
△ ← flecha hueca = herencia
┌────────┴────────┐
│ │
┌───────┴──────┐ ┌──────┴────────┐
│ Perro │ │ Gato │
├──────────────┤ ├───────────────┤
│ - raza:String│ │-esIndoor:bool │
├──────────────┤ ├───────────────┤
│+hacerSonido()│ │+hacerSonido() │
│+buscarPelota │ │+ronronear() │
└──────────────┘ └───────────────┘
⚠️ Regla UML: La flecha apunta siempre hacia la superclase. Se lee: “Perro extends Animal”.
3.2 Código Java
Superclase Animal:
public class Animal {
private String nombre;
private int edad;
public Animal(String nombre, int edad) {
this.nombre = nombre;
this.edad = edad;
}
public String getNombre() { return nombre; }
public void hacerSonido() {
System.out.println("El animal hace un sonido");
}
}
Subclase Perro:
public class Perro extends Animal {
private String raza;
public Perro(String nombre, int edad, String raza) {
super(nombre, edad); // llama al constructor padre
this.raza = raza;
}
@Override
public void hacerSonido() {
System.out.println("¡Guau guau!");
}
public void buscarPelota() {
System.out.println(getNombre() + " busca la pelota");
}
}
Subclase Gato:
public class Gato extends Animal {
private boolean esIndoor;
public Gato(String nombre, int edad, boolean esIndoor) {
super(nombre, edad);
this.esIndoor = esIndoor;
}
@Override
public void hacerSonido() {
System.out.println("¡Miau!");
}
public void ronronear() {
System.out.println(getNombre() + " ronronea...");
}
}
4. Interfaces y Clases Abstractas
4.1 Diferencias Clave
| Característica | interface | Clase abstract |
|---|---|---|
| Notación UML | «interface» | «abstract» |
| Flecha UML | Línea discontinua con triángulo | Línea sólida con triángulo |
| Keyword Java | implements | extends |
| Herencia múltiple | ✅ Una clase puede implementar N interfaces | ❌ Solo extiende 1 clase abstracta |
| Atributos | Solo constantes (static final) | Atributos normales |
Métodos default | ✅ Desde Java 8 | ✅ Siempre |
4.2 Notación UML
«interface» «abstract»
┌──────────────┐ ┌─────────────────────────┐
│ Volable │ │ Figura │
├──────────────┤ ├─────────────────────────┤
│ │ │ # color: String │
├──────────────┤ ├─────────────────────────┤
│+volar(): void│ │ + getColor(): String │
│+aterrizar() │ │ + calcularArea(): double│
└──────┬───────┘ │ {abstract} │
△ (línea discontinua) └───────────┬─────────────┘
┌────┴────┐ △ (línea sólida)
│ │ │
┌─┴──────┐ ┌┴──────┐ ┌─────┴──────┐
│ Aguila │ │ Avion │ │ Circulo │
└────────┘ └───────┘ └────────────┘
4.3 Código Java
Interface Volable:
public interface Volable {
void volar(); // implícitamente public abstract
void aterrizar();
// Método default (Java 8+)
default void despegar() {
System.out.println("Preparando despegue...");
volar();
}
}
Clase Abstracta Figura:
public abstract class Figura {
protected String color; // # en UML = protected
public Figura(String color) {
this.color = color;
}
public String getColor() { return color; }
// Método abstracto — las subclases DEBEN implementarlo
public abstract double calcularArea();
}
Clase Circulo (extends + implements):
public class Circulo extends Figura {
private double radio;
public Circulo(String color, double radio) {
super(color);
this.radio = radio;
}
@Override
public double calcularArea() {
return Math.PI * radio * radio;
}
}
Clase Aguila (implements interface):
public class Aguila implements Volable {
private double envergadura;
public Aguila(double envergadura) {
this.envergadura = envergadura;
}
@Override public void volar() { System.out.println("El águila planea"); }
@Override public void aterrizar() { System.out.println("El águila aterriza"); }
}
5. Relaciones entre Clases
5.1 Tipos de Relaciones
| Relación | Símbolo UML | Descripción |
|---|---|---|
| Herencia | ──────▷ (sólida, triángulo hueco) | “Es un” — extends |
| Realización | - - - ▷ (discontinua, triángulo hueco) | Implementa interface — implements |
| Asociación | ──────> (sólida, flecha abierta) | “Conoce a” — campo de referencia |
| Agregación | ──────◇ (rombo hueco) | “Tiene un” — ciclo de vida independiente |
| Composición | ──────◆ (rombo sólido) | “Compuesto por” — vida dependiente del todo |
| Dependencia | - - - -> (discontinua, flecha) | Uso temporal — parámetro o variable local |
5.2 Diagrama de Relaciones
┌──────────────┐ 1 1..* ┌──────────────┐ 1 1..* ┌──────────────┐
│ Universidad │◆───────────│ Departamento │────────────│ Profesor │
└──────────────┘ composición└──────────────┘ asociación └──────────────┘
┌──────────────┐ 0..* 0..* ┌──────────────┐
│ Estudiante │─────────────│ Curso │
└──────────────┘ asociación └──────────────┘
5.3 Multiplicidades
| Notación | Significado |
|---|---|
1 | Exactamente uno |
0..1 | Cero o uno (opcional) |
* o 0..* | Cero o muchos |
1..* | Uno o muchos (al menos uno) |
2..5 | Entre dos y cinco |
n | Exactamente n |
5.4 Código Java para cada Relación
Asociación — una clase referencia a otra:
// Departamento ───> Profesor (asociación)
public class Departamento {
private String nombre;
private List<Profesor> profesores; // 1..* profesores
public void agregarProfesor(Profesor p) {
profesores.add(p);
}
}
Composición — la parte no puede existir sin el todo:
// Universidad ◆─── Departamento (composición)
public class Universidad {
private final List<Departamento> departamentos = new ArrayList<>();
public void agregarDepto(String nombre) {
// El Departamento se crea AQUÍ — su vida depende de Universidad
departamentos.add(new Departamento(nombre));
}
}
Agregación — la parte puede existir de forma independiente:
// Equipo ◇─── Jugador (agregación)
public class Equipo {
private List<Jugador> jugadores; // los jugadores existen fuera del equipo
public Equipo(List<Jugador> jugadores) {
this.jugadores = jugadores; // recibe referencias externas
}
}
Dependencia — uso temporal en un método:
// Reporte - - -> Formatter (dependencia)
public class Reporte {
public void generar(Formatter formatter) { // uso temporal
String contenido = formatter.formatear(this);
System.out.println(contenido);
}
}
6. Patrones de Diseño en UML
6.1 Patrón Singleton
Garantiza que una clase tenga una única instancia y proporciona un punto de acceso global. En UML el atributo y método estáticos se representan subrayados.
┌──────────────────────────────────────┐
│ ConfiguracionApp │
├──────────────────────────────────────┤
│ - instancia: ConfiguracionApp {static}│
│ - idioma: String │
│ - tema: String │
├──────────────────────────────────────┤
│ - ConfiguracionApp() │
│ + getInstance(): ConfiguracionApp │
│ {static} │
│ + getIdioma(): String │
│ + setIdioma(s: String): void │
└──────────────────────────────────────┘
public class ConfiguracionApp {
private static ConfiguracionApp instancia; // {static} en UML
private String idioma;
private String tema;
private ConfiguracionApp() { // constructor privado (- en UML)
this.idioma = "es";
this.tema = "claro";
}
public static ConfiguracionApp getInstance() {
if (instancia == null) {
instancia = new ConfiguracionApp();
}
return instancia;
}
public String getIdioma() { return idioma; }
public void setIdioma(String idioma) { this.idioma = idioma; }
}
// Uso
ConfiguracionApp config = ConfiguracionApp.getInstance();
config.setIdioma("en");
6.2 Patrón Factory Method
Define una interfaz para crear objetos, pero deja que las subclases o una factory decidan qué clase instanciar.
«interface»
┌────────────────────┐
│ Notificacion │
├────────────────────┤
│ + enviar(s:String) │
└──────────┬─────────┘
△
┌──────┴──────┐
│ │
┌───┴────────┐ ┌──┴──────────────┐
│Notificacion│ │ NotificacionSMS │
│ Email │ ├─────────────────┤
├────────────┤ │+enviar(s:String)│
│+enviar(s) │ └─────────────────┘
└────────────┘
┌───────────────────────────┐
│ NotificacionFactory │
├───────────────────────────┤
│+crear(tipo:String): │
│ Notificacion {static} │
└───────────────────────────┘
public interface Notificacion {
void enviar(String mensaje);
}
public class NotificacionEmail implements Notificacion {
@Override
public void enviar(String mensaje) {
System.out.println("Email: " + mensaje);
}
}
public class NotificacionSMS implements Notificacion {
@Override
public void enviar(String mensaje) {
System.out.println("SMS: " + mensaje);
}
}
public class NotificacionFactory {
public static Notificacion crear(String tipo) {
return switch (tipo) {
case "email" -> new NotificacionEmail();
case "sms" -> new NotificacionSMS();
default -> throw new IllegalArgumentException("Tipo desconocido: " + tipo);
};
}
}
// Uso
Notificacion n = NotificacionFactory.crear("email");
n.enviar("¡Bienvenido al sistema!");
7. Generics y Colecciones en UML
Los genéricos se representan en UML con parámetros de tipo entre corchetes angulares: Repositorio<T>.
7.1 Clase Genérica
┌──────────────────────────┐
│ Repositorio<T> │
├──────────────────────────┤
│ - elementos: List<T> │
├──────────────────────────┤
│ + agregar(e: T): void │
│ + obtener(i: int): T │
│ + obtenerTodos(): List<T>│
│ + contar(): int │
└──────────────────────────┘
public class Repositorio<T> {
private List<T> elementos = new ArrayList<>();
public void agregar(T elemento) { elementos.add(elemento); }
public T obtener(int indice) { return elementos.get(indice); }
public List<T> obtenerTodos() { return Collections.unmodifiableList(elementos); }
public int contar() { return elementos.size(); }
}
// Uso tipado
Repositorio<Persona> repo = new Repositorio<>();
repo.agregar(new Persona("Ana", 22, "ana@uni.edu"));
System.out.println(repo.contar()); // 1
7.2 Bounded Generics
// <T extends Figura> — T debe ser Figura o alguna subclase
public class LienzoDibujo<T extends Figura> {
private List<T> figuras = new ArrayList<>();
public void agregar(T figura) { figuras.add(figura); }
public double areaTotal() {
return figuras.stream()
.mapToDouble(Figura::calcularArea)
.sum();
}
}
// Uso
LienzoDibujo<Circulo> lienzo = new LienzoDibujo<>();
lienzo.agregar(new Circulo("rojo", 5.0));
lienzo.agregar(new Circulo("azul", 3.0));
System.out.println("Área total: " + lienzo.areaTotal());
8. Caso de Estudio Completo — Sistema Biblioteca
Aplicamos todos los conceptos en un sistema de gestión de biblioteca que integra herencia, interfaces, composición y multiplicidad.
8.1 Descripción del Sistema
- La biblioteca contiene muchos libros y tiene socios registrados (composición).
- Los libros pueden ser físicos o digitales (herencia).
- Los socios realizan préstamos (asociación).
- Existe una interface
Prestableque implementan los libros.
8.2 Diagrama UML del Sistema
«interface»
┌──────────────────┐
│ Prestable │
├──────────────────┤
│+estaDisponible() │
│+prestar(s:Socio) │
│+devolver() │
└────────┬─────────┘
△ (implements, línea discontinua)
│
┌────────┴───────────┐
│ Libro │ «abstract»
├────────────────────┤
│ - isbn: String │
│ - titulo: String │
│ - autor: String │
│ - disponible: bool │
├────────────────────┤
│+getTitulo():String │
│+getTipo():String │
│ {abstract} │
└──────┬─────────────┘
△
┌────┴────────┐
│ │
┌─┴──────────┐ ┌┴────────────┐
│ LibroFisico│ │ LibroDigital│
└────────────┘ └─────────────┘
┌──────────────────┐ 1 0..* ┌───────────────┐
│ Biblioteca │◆────────────│ Libro │
├──────────────────┤ └───────────────┘
│ - nombre: String │ 1 0..* ┌───────────────┐
│ │◆────────────│ Socio │
│+realizarPrestamo │ └───────┬───────┘
│+prestamosActivos │ │ 0..*
└──────────────────┘ │
┌──────┴──────┐
│ Prestamo │
└─────────────┘
8.3 Código Completo
Interface Prestable:
public interface Prestable {
boolean estaDisponible();
void prestar(Socio socio);
void devolver();
}
Clase abstracta Libro:
public abstract class Libro implements Prestable {
private final String isbn;
private final String titulo;
private final String autor;
private boolean disponible = true;
public Libro(String isbn, String titulo, String autor) {
this.isbn = isbn;
this.titulo = titulo;
this.autor = autor;
}
public String getIsbn() { return isbn; }
public String getTitulo() { return titulo; }
public String getAutor() { return autor; }
@Override public boolean estaDisponible() { return disponible; }
@Override public void devolver() { disponible = true; }
@Override
public void prestar(Socio socio) {
if (!disponible) throw new IllegalStateException("Libro no disponible");
disponible = false;
}
// Método abstracto — subclases deben implementarlo
public abstract String getTipo();
}
Clase LibroFisico:
public class LibroFisico extends Libro {
private int numeroPaginas;
private String ubicacionEstante;
public LibroFisico(String isbn, String titulo, String autor,
int paginas, String estante) {
super(isbn, titulo, autor);
this.numeroPaginas = paginas;
this.ubicacionEstante = estante;
}
@Override public String getTipo() { return "Físico"; }
public String getUbicacion() { return ubicacionEstante; }
}
Clase LibroDigital:
public class LibroDigital extends Libro {
private String urlDescarga;
private double tamanoMB;
public LibroDigital(String isbn, String titulo, String autor,
String url, double tamanoMB) {
super(isbn, titulo, autor);
this.urlDescarga = url;
this.tamanoMB = tamanoMB;
}
@Override public String getTipo() { return "Digital"; }
public String getUrl() { return urlDescarga; }
}
Clase Socio:
public class Socio {
private final String id;
private String nombre;
private final List<Prestamo> historialPrestamos = new ArrayList<>();
public Socio(String id, String nombre) {
this.id = id;
this.nombre = nombre;
}
public String getId() { return id; }
public String getNombre() { return nombre; }
public void registrarPrestamo(Prestamo p) {
historialPrestamos.add(p);
}
public List<Prestamo> getHistorial() {
return Collections.unmodifiableList(historialPrestamos);
}
}
Clase Prestamo:
import java.time.LocalDate;
public class Prestamo {
private final Libro libro;
private final Socio socio;
private final LocalDate fechaPrestamo;
private LocalDate fechaDevolucion;
private boolean activo = true;
public Prestamo(Libro libro, Socio socio) {
this.libro = libro;
this.socio = socio;
this.fechaPrestamo = LocalDate.now();
libro.prestar(socio);
socio.registrarPrestamo(this);
}
public void cerrar() {
libro.devolver();
this.fechaDevolucion = LocalDate.now();
this.activo = false;
}
public boolean isActivo() { return activo; }
public Libro getLibro() { return libro; }
public LocalDate getFechaPrestamo() { return fechaPrestamo; }
public LocalDate getFechaDevolucion() { return fechaDevolucion; }
}
Clase Biblioteca (composición central):
public class Biblioteca {
private final String nombre;
private final List<Libro> catalogo = new ArrayList<>();
private final List<Socio> socios = new ArrayList<>();
private final List<Prestamo> prestamos = new ArrayList<>();
public Biblioteca(String nombre) { this.nombre = nombre; }
public void agregarLibro(Libro libro) { catalogo.add(libro); }
public void registrarSocio(Socio s) { socios.add(s); }
public Prestamo realizarPrestamo(String isbn, String idSocio) {
Libro libro = catalogo.stream()
.filter(l -> l.getIsbn().equals(isbn) && l.estaDisponible())
.findFirst()
.orElseThrow(() -> new RuntimeException("Libro no disponible: " + isbn));
Socio socio = socios.stream()
.filter(s -> s.getId().equals(idSocio))
.findFirst()
.orElseThrow(() -> new RuntimeException("Socio no encontrado: " + idSocio));
Prestamo p = new Prestamo(libro, socio);
prestamos.add(p);
return p;
}
public long prestamosActivos() {
return prestamos.stream().filter(Prestamo::isActivo).count();
}
}
Programa principal de prueba:
public class Main {
public static void main(String[] args) {
Biblioteca biblioteca = new Biblioteca("Biblioteca Central");
// Agregar libros
biblioteca.agregarLibro(new LibroFisico("978-0-13-468599-1",
"Clean Code", "Robert C. Martin", 431, "A-12"));
biblioteca.agregarLibro(new LibroDigital("978-0-596-51774-8",
"Head First Java", "Kathy Sierra", "http://ejemplo.com/hfj.pdf", 45.2));
// Registrar socios
biblioteca.registrarSocio(new Socio("S001", "María García"));
biblioteca.registrarSocio(new Socio("S002", "Carlos López"));
// Realizar préstamo
Prestamo p = biblioteca.realizarPrestamo("978-0-13-468599-1", "S001");
System.out.println("Préstamos activos: " + biblioteca.prestamosActivos()); // 1
// Devolver
p.cerrar();
System.out.println("Préstamos activos: " + biblioteca.prestamosActivos()); // 0
}
}
9. Referencia Rápida UML → Java
| Notación UML | Código Java |
|---|---|
+ atributo: Tipo | public Tipo atributo; |
- atributo: Tipo | private Tipo atributo; |
# atributo: Tipo | protected Tipo atributo; |
+ metodo(): void | public void metodo() {} |
+ metodo(): Tipo {abstract} | public abstract Tipo metodo(); |
+ metodo(): Tipo {static} | public static Tipo metodo() {} |
ClaseA ──▷ ClaseB (herencia) | class ClaseA extends ClaseB |
ClaseA - - ▷ InterfazB (realización) | class ClaseA implements InterfazB |
ClaseA ──── ClaseB (asociación) | ClaseA tiene un campo de tipo ClaseB |
ClaseA ◆─── ClaseB (composición) | ClaseA crea instancias de ClaseB internamente |
ClaseA ◇─── ClaseB (agregación) | ClaseA recibe referencias externas a ClaseB |
Clase<T> | class Clase<T> { ... } |
«interface» | public interface Nombre |
«abstract» | public abstract class Nombre |
1..* | List<Tipo> (al menos un elemento) |
0..1 | Optional<Tipo> o campo nullable |
10. Buenas Prácticas y Errores Comunes
10.1 Buenas Prácticas
- Modelar antes de codificar: el diagrama es el contrato del sistema.
- Nombres en singular para clases:
Persona, noPersonas. - Encapsulación siempre: atributos privados (
-), acceso mediante métodos públicos. - Preferir composición sobre herencia cuando no existe una relación “es un”.
- Limitar la herencia a 2–3 niveles máximo para mantener legibilidad.
- Documentar multiplicidades: definen reglas de negocio directamente en el diagrama.
- Interfaces para contratos, clases abstractas para comportamiento parcial compartido.
- Principio de responsabilidad única (SRP): cada clase debe tener un único motivo para cambiar.
10.2 Errores Comunes
| Error | Corrección |
|---|---|
| Clase con demasiados atributos/métodos | Aplicar SRP: dividir en clases con responsabilidades únicas |
| Herencia usada solo para reutilizar código | Usar composición o interfaces con default methods |
| Olvidar multiplicidades en relaciones | Anotar siempre la cardinalidad en ambos extremos |
| Confundir agregación con composición | Composición: la parte no existe sin el todo; en agregación sí |
| Ciclos de dependencia entre paquetes | Introducir una interface para romper el ciclo |
| Clase con múltiples herencias | Java no lo permite; usar múltiples interfaces en su lugar |
Apéndice — Símbolos UML de Referencia
| Símbolo | Nombre | Uso |
|---|---|---|
──────▷ | Herencia / Generalización | extends — flecha sólida, triángulo hueco |
- - - ▷ | Realización | implements — flecha discontinua, triángulo hueco |
──────> | Asociación | Referencia directa entre clases |
──────◇ | Agregación | “Tiene un”, ciclo de vida independiente (rombo hueco) |
──────◆ | Composición | “Compuesto por”, vida dependiente (rombo sólido) |
- - - -> | Dependencia | Uso temporal: parámetro o variable local |
«interface» | Estereotipo de interfaz | Clase que define un contrato |
«abstract» | Estereotipo de clase abstracta | Clase que no puede instanciarse directamente |
{abstract} | Restricción de método | El método no tiene implementación en esta clase |
{static} | Restricción estática | Miembro de clase, no de instancia (subrayado en UML) |
1, 0..1, *, 1..* | Multiplicidades | Cardinalidad en los extremos de una relación |
📚 Referencias:
- Especificación oficial UML 2.5: omg.org
- Oracle Java Documentation: docs.oracle.com
- Design Patterns — Gamma, Helm, Johnson, Vlissides (Gang of Four)
- Clean Code — Robert C. Martin