Ir al contenido principal

Conversión y casting de tipos de datos

Introducción

En Java, como en muchos lenguajes de programación fuertemente tipados, cada variable tiene un tipo de dato definido que determina qué clase de valores puede almacenar y qué operaciones se pueden realizar con ella. Sin embargo, durante el desarrollo de aplicaciones, es común necesitar convertir datos de un tipo a otro. Por ejemplo, podríamos necesitar convertir un número entero a decimal, un decimal a entero, o incluso transformar una cadena de texto en un valor numérico. Java proporciona diversos mecanismos para realizar estas conversiones, que se conocen como conversión de tipos o "casting". En este artículo, exploraremos en detalle cómo funcionan estas conversiones, cuándo se producen automáticamente, cuándo es necesario realizarlas de forma explícita y las mejores prácticas para evitar errores comunes.

Tipos de datos en Java

Antes de profundizar en la conversión de tipos, repasemos brevemente los tipos de datos disponibles en Java:

Tipos primitivos

  1. Tipos enteros:

    • byte: 8 bits, rango de -128 a 127
    • short: 16 bits, rango de -32,768 a 32,767
    • int: 32 bits, rango de -2^31 a 2^31-1
    • long: 64 bits, rango de -2^63 a 2^63-1
  2. Tipos de punto flotante:

    • float: 32 bits, precisión simple
    • double: 64 bits, precisión doble
  3. Otros tipos primitivos:

    • char: 16 bits, representa un carácter Unicode
    • boolean: representa un valor verdadero o falso

Tipos de referencia

  • Clases: String, Integer, Double, etc.
  • Interfaces
  • Arrays

Conversión implícita (automática)

La conversión implícita sigue una jerarquía de tipos, que en Java se ve así:

byte → short → int → long → float → double
        ↑
       char

Veamos algunos ejemplos:

public class ConversionImplicita {
    public static void main(String[] args) {
        // De byte a int
        byte numeroPequeno = 10;
        int numeroGrande = numeroPequeno;  // Conversión implícita de byte a int
        System.out.println("byte a int: " + numeroGrande);
        
        // De int a double
        int entero = 100;
        double decimal = entero;  // Conversión implícita de int a double
        System.out.println("int a double: " + decimal);  // 100.0
        
        // De char a int
        char caracter = 'A';
        int valorCaracter = caracter;  // Conversión implícita de char a int (valor ASCII/Unicode)
        System.out.println("char '" + caracter + "' a int: " + valorCaracter);  // 65
        
        // De int a long
        int numeroInt = 2147483647;  // Valor máximo para int
        long numeroLong = numeroInt;  // Conversión implícita de int a long
        System.out.println("int a long: " + numeroLong);
        
        // Conversión implícita en expresiones mixtas
        int x = 5;
        double y = 2.5;
        double resultado = x + y;  // x se convierte implícitamente a double
        System.out.println("int + double: " + resultado);  // 7.5
    }
}

Casos especiales de conversión implícita

Cuando se trabaja con literales enteros en expresiones

Java trata los literales enteros como int por defecto, pero en ciertos contextos puede realizar conversiones implícitas:

byte b = 10;  // Aunque 10 es un literal int, cabe en un byte
short s = 200;  // Aunque 200 es un literal int, cabe en un short

// Esto NO compila:
// byte errorB = 200;  // 200 está fuera del rango de byte (-128 a 127)

Cuando se trabaja con expresiones aritméticas

En expresiones aritméticas, los tipos menores que int se promocionan automáticamente a int:

byte b1 = 10;
byte b2 = 20;
// byte suma = b1 + b2;  // Error de compilación, b1 + b2 es de tipo int
int suma = b1 + b2;  // Correcto

Conversión explícita (casting)

Cuando queremos convertir un tipo a otro que no sigue la jerarquía de promoción automática, o cuando la conversión puede resultar en pérdida de datos, Java requiere que hagamos un casting explícito. La sintaxis para el casting es colocar el tipo de destino entre paréntesis delante de la expresión a convertir:

(tipoDato) expresion

Conversión de tipos numéricos

public class ConversionExplicita {
    public static void main(String[] args) {
        // De double a int (puede perder la parte decimal)
        double decimal = 9.8;
        int entero = (int) decimal;
        System.out.println("double a int: " + decimal + " → " + entero);  // 9.8 → 9
        
        // De long a int (puede perder precisión si el valor no cabe en un int)
        long numeroGrande = 2147483648L;  // Valor mayor que el máximo de int
        int numeroTruncado = (int) numeroGrande;
        System.out.println("long a int: " + numeroGrande + " → " + numeroTruncado);  // Da un resultado no esperado
        
        // De float a int
        float flotante = 5.6f;
        int enteroDesdeFloat = (int) flotante;
        System.out.println("float a int: " + flotante + " → " + enteroDesdeFloat);  // 5.6 → 5
        
        // De int a byte (recorta bits extras, puede causar desbordamiento)
        int valorInt = 130;
        byte valorByte = (byte) valorInt;
        System.out.println("int a byte: " + valorInt + " → " + valorByte);  // 130 → -126 (desbordamiento)
        
        // Casting múltiple
        double valorDouble = 295.04;
        int valorInt2 = (int) valorDouble;
        byte valorByte2 = (byte) valorInt2;
        System.out.println("double a int a byte: " + valorDouble + " → " + valorInt2 + " → " + valorByte2);
    }
}

Consecuencias del casting incorrecto

Como hemos visto en los ejemplos anteriores, el casting puede provocar pérdida de información o resultados inesperados:

  1. Truncamiento: Al convertir de punto flotante a entero, se pierde la parte decimal.
  2. Desbordamiento: Si convertimos un valor que está fuera del rango del tipo destino, se produce un desbordamiento.
  3. Pérdida de precisión: Al convertir de double a float, podemos perder precisión.

Es importante entender estas consecuencias para evitar errores difíciles de detectar.

Conversión de tipos char

El tipo char merece una mención especial porque representa un carácter Unicode pero internamente se maneja como un valor numérico.

public class ConversionChar {
    public static void main(String[] args) {
        // De char a int (implícito)
        char letra = 'Z';
        int valorLetra = letra;
        System.out.println("char a int: '" + letra + "' → " + valorLetra);  // 'Z' → 90
        
        // De int a char (explícito)
        int codigo = 65;
        char caracter = (char) codigo;
        System.out.println("int a char: " + codigo + " → '" + caracter + "'");  // 65 → 'A'
        
        // Incrementando un char
        char c1 = 'a';
        char c2 = (char)(c1 + 1);
        System.out.println("Incremento de char: '" + c1 + "' + 1 = '" + c2 + "'");  // 'a' + 1 = 'b'
    }
}

Tipos de referencia y casting

El casting también puede aplicarse a tipos de referencia (objetos), pero con reglas diferentes. En este caso, el casting verifica si el objeto puede ser tratado como una instancia del tipo destino.

// Ejemplo básico de herencia
class Animal {
    void hacerSonido() {
        System.out.println("Algún sonido animal");
    }
}

class Perro extends Animal {
    @Override
    void hacerSonido() {
        System.out.println("Guau guau");
    }
    
    void moverCola() {
        System.out.println("Moviendo la cola");
    }
}

public class CastingReferencia {
    public static void main(String[] args) {
        // Conversión implícita (de subclase a superclase)
        Perro miPerro = new Perro();
        Animal miAnimal = miPerro;  // Conversión implícita de Perro a Animal
        miAnimal.hacerSonido();  // "Guau guau" (polimorfismo)
        
        // Conversión explícita (de superclase a subclase)
        Animal otroAnimal = new Perro();
        // otroAnimal.moverCola();  // Error: Animal no tiene este método
        
        // Necesitamos casting para acceder a métodos específicos de la subclase
        Perro otroPerro = (Perro) otroAnimal;
        otroPerro.moverCola();  // Ahora podemos llamar a métodos específicos de Perro
        
        // ¡Cuidado! El casting fallará en tiempo de ejecución si el objeto real no es compatible
        Animal unGato = new Animal();  // Este es realmente un Animal, no un Perro
        
        try {
            Perro perroImposible = (Perro) unGato;  // Esto lanzará ClassCastException
            perroImposible.moverCola();
        } catch (ClassCastException e) {
            System.out.println("Error: No puedes convertir un Animal genérico a Perro");
            System.out.println("Mensaje: " + e.getMessage());
        }
    }
}

Operador instanceof

Para evitar errores de casting en tiempo de ejecución, Java proporciona el operador instanceof que permite verificar si un objeto es una instancia de un tipo específico antes de intentar el casting:

public class UsoInstanceof {
    public static void main(String[] args) {
        Animal miAnimal = new Perro();
        
        // Verificamos el tipo antes de hacer el casting
        if (miAnimal instanceof Perro) {
            Perro miPerro = (Perro) miAnimal;
            miPerro.moverCola();
        } else {
            System.out.println("Este animal no es un perro");
        }
        
        Animal otroAnimal = new Animal();
        
        if (otroAnimal instanceof Perro) {
            Perro perro = (Perro) otroAnimal;
            perro.moverCola();
        } else {
            System.out.println("Este animal no es un perro");  // Se ejecutará esta línea
        }
    }
}

A partir de Java 16, existe un nuevo patrón llamado "pattern matching for instanceof" que simplifica este tipo de código:

// Con Java 16+
public class PatternMatchingInstanceof {
    public static void main(String[] args) {
        Animal miAnimal = new Perro();
        
        // Pattern matching para instanceof
        if (miAnimal instanceof Perro miPerro) {
            // Aquí miPerro ya está disponible como variable de tipo Perro
            miPerro.moverCola();
        }
    }
}

Conversión entre tipos primitivos y tipos de referencia

Java proporciona clases envoltorio (wrapper classes) para cada tipo primitivo:

Tipo primitivo Clase envoltorio
boolean Boolean
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character

Autoboxing y Unboxing

A partir de Java 5, la conversión entre tipos primitivos y sus clases envoltorio se realiza automáticamente mediante autoboxing (primitivo a envoltorio) y unboxing (envoltorio a primitivo):

public class AutoboxingUnboxing {
    public static void main(String[] args) {
        // Autoboxing (primitivo a objeto)
        int primitivo = 42;
        Integer objeto = primitivo;  // Autoboxing
        System.out.println("Autoboxing: " + primitivo + " → " + objeto);
        
        // Unboxing (objeto a primitivo)
        Integer numero = 100;
        int valor = numero;  // Unboxing
        System.out.println("Unboxing: " + numero + " → " + valor);
        
        // Autoboxing/unboxing en expresiones
        Integer a = 10;
        Integer b = 20;
        Integer c = a + b;  // a y b se desenvuelven, se suman, y luego el resultado se envuelve nuevamente
        System.out.println("a + b = " + c);
        
        // Autoboxing/unboxing con otros tipos
        Double d = 3.14;
        double pd = d;  // Unboxing
        
        Character ch = 'X';
        char pch = ch;  // Unboxing
        
        Boolean bool = true;
        boolean pbool = bool;  // Unboxing
        
        // En colecciones (que requieren objetos, no primitivos)
        ArrayList<Integer> numeros = new ArrayList<>();
        numeros.add(1);  // Autoboxing: el 1 (int) se convierte a Integer
        numeros.add(2);
        numeros.add(3);
        
        int sum = 0;
        for (Integer num : numeros) {
            sum += num;  // Unboxing: cada Integer se convierte a int
        }
        System.out.println("Suma de números en ArrayList: " + sum);
    }
}

Conversión explícita usando métodos

Aunque el autoboxing y unboxing simplifican el código, a veces necesitamos un control más explícito. Todas las clases envoltorio proporcionan métodos para convertir entre tipos:

public class ConversionExplicitaEnvoltorio {
    public static void main(String[] args) {
        // Conversión de String a tipos primitivos
        String numeroStr = "123";
        int numeroInt = Integer.parseInt(numeroStr);
        double numeroDouble = Double.parseDouble(numeroStr);
        boolean booleano = Boolean.parseBoolean("true");
        
        System.out.println("String a int: \"" + numeroStr + "\" → " + numeroInt);
        System.out.println("String a double: \"" + numeroStr + "\" → " + numeroDouble);
        System.out.println("String a boolean: \"true\" → " + booleano);
        
        // Conversión de primitivos a String
        int valor = 456;
        String valorStr = Integer.toString(valor);
        String valorStr2 = String.valueOf(valor);  // Alternativa
        
        System.out.println("int a String: " + valor + " → \"" + valorStr + "\"");
        System.out.println("int a String (valueOf): " + valor + " → \"" + valorStr2 + "\"");
        
        // Conversión entre tipos numéricos usando clases envoltorio
        double d = 123.456;
        int i = Double.valueOf(d).intValue();  // Double → int (con objeto intermedio)
        
        System.out.println("double a int (via clase envoltorio): " + d + " → " + i);
    }
}

Conversión de String a otros tipos y viceversa

La conversión entre String y otros tipos de datos es una operación muy común en Java:

De String a tipos primitivos

public class StringAOtrosTipos {
    public static void main(String[] args) {
        // De String a varios tipos primitivos
        String textoNumero = "42";
        
        // A int
        int entero = Integer.parseInt(textoNumero);
        System.out.println("String a int: \"" + textoNumero + "\" → " + entero);
        
        // A double
        double decimal = Double.parseDouble(textoNumero);
        System.out.println("String a double: \"" + textoNumero + "\" → " + decimal);
        
        // A long
        long numeroLargo = Long.parseLong(textoNumero);
        System.out.println("String a long: \"" + textoNumero + "\" → " + numeroLargo);
        
        // A byte
        byte numeroByte = Byte.parseByte(textoNumero);
        System.out.println("String a byte: \"" + textoNumero + "\" → " + numeroByte);
        
        // A boolean (true si el string es "true", ignorando mayúsculas/minúsculas)
        boolean valorBooleano1 = Boolean.parseBoolean("true");
        boolean valorBooleano2 = Boolean.parseBoolean("TRUE");
        boolean valorBooleano3 = Boolean.parseBoolean("false");
        boolean valorBooleano4 = Boolean.parseBoolean("algo");
        
        System.out.println("String a boolean: \"true\" → " + valorBooleano1);
        System.out.println("String a boolean: \"TRUE\" → " + valorBooleano2);
        System.out.println("String a boolean: \"false\" → " + valorBooleano3);
        System.out.println("String a boolean: \"algo\" → " + valorBooleano4);
        
        // A char (tomando el primer carácter)
        char caracter = "Hola".charAt(0);
        System.out.println("Primer carácter de String: \"Hola\" → '" + caracter + "'");
        
        try {
            // Manejo de errores: intento de parsear un String no numérico
            int numeroError = Integer.parseInt("abc");
        } catch (NumberFormatException e) {
            System.out.println("Error: No se puede convertir \"abc\" a int");
        }
    }
}

De otros tipos a String

public class OtrosTiposAString {
    public static void main(String[] args) {
        // De varios tipos a String
        
        // De int a String
        int numero = 123;
        String textoNumero1 = Integer.toString(numero);
        String textoNumero2 = String.valueOf(numero);
        String textoNumero3 = "" + numero;  // Concatenación (menos eficiente)
        
        System.out.println("int a String (toString): " + numero + " → \"" + textoNumero1 + "\"");
        System.out.println("int a String (valueOf): " + numero + " → \"" + textoNumero2 + "\"");
        System.out.println("int a String (concatenación): " + numero + " → \"" + textoNumero3 + "\"");
        
        // De double a String
        double decimal = 123.456;
        String textoDecimal = Double.toString(decimal);
        System.out.println("double a String: " + decimal + " → \"" + textoDecimal + "\"");
        
        // De boolean a String
        boolean flag = true;
        String textoBooleano = Boolean.toString(flag);
        System.out.println("boolean a String: " + flag + " → \"" + textoBooleano + "\"");
        
        // De char a String
        char letra = 'A';
        String textoLetra = Character.toString(letra);
        System.out.println("char a String: '" + letra + "' → \"" + textoLetra + "\"");
        
        // De objeto a String (usando toString())
        Date fecha = new Date();
        String textoFecha = fecha.toString();
        System.out.println("Date a String: " + fecha + " → \"" + textoFecha + "\"");
    }
}

import java.util.Date;  // Requerido para el último ejemplo

Formateo avanzado con String.format()

Para conversiones más complejas o con formato específico, podemos usar String.format():

public class FormateoAvanzado {
    public static void main(String[] args) {
        // Formateando números
        double valor = 1234.56789;
        
        // Con dos decimales
        String dosDecimales = String.format("%.2f", valor);
        System.out.println("Formato con dos decimales: " + dosDecimales);
        
        // Con separador de miles
        String conSeparador = String.format("%,.2f", valor);
        System.out.println("Formato con separador: " + conSeparador);
        
        // Ancho fijo y alineación
        String alineadoDerecha = String.format("%10.2f", valor);
        System.out.println("Alineado a la derecha (10 espacios): '" + alineadoDerecha + "'");
        
        // Diferentes bases numéricas
        int numero = 255;
        String hexadecimal = String.format("%X", numero);  // Hexadecimal en mayúsculas
        String octal = String.format("%o", numero);        // Octal
        String binario = Integer.toBinaryString(numero);   // Binario (no hay especificador de formato)
        
        System.out.println("Decimal a hexadecimal: " + numero + " → " + hexadecimal);
        System.out.println("Decimal a octal: " + numero + " → " + octal);
        System.out.println("Decimal a binario: " + numero + " → " + binario);
        
        // Formateando fechas
        Date ahora = new Date();
        String fechaFormateada = String.format("%tF %tT", ahora, ahora);  // YYYY-MM-DD HH:MM:SS
        System.out.println("Fecha formateada: " + fechaFormateada);
    }
}

import java.util.Date;  // Requerido para el último ejemplo

Mejores prácticas y consideraciones

Cuándo usar casting explícito vs. métodos de conversión

  • Casting explícito: Ideal para conversiones sencillas entre tipos primitivos compatibles.
  • Métodos de conversión: Preferibles para conversiones entre String y otros tipos, o cuando necesitas controlar el comportamiento ante valores no válidos.

Evitar pérdida de datos

Siempre que sea posible, evita conversiones que puedan resultar en pérdida de datos:

long numeroGrande = 9999999999L;  // Este número no cabe en un int

// Mal: pérdida de datos sin verificación
int numeroTruncado = (int) numeroGrande;

// Mejor: verificar antes de convertir
if (numeroGrande <= Integer.MAX_VALUE && numeroGrande >= Integer.MIN_VALUE) {
    int numeroSeguro = (int) numeroGrande;
    System.out.println("Conversión segura: " + numeroSeguro);
} else {
    System.out.println("El valor está fuera del rango de int");
}

Manejo de excepciones en conversiones

Cuando conviertes desde String a tipos numéricos, siempre considera la posibilidad de una NumberFormatException:

String entrada = "123abc";

try {
    int numero = Integer.parseInt(entrada);
    System.out.println("Número convertido: " + numero);
} catch (NumberFormatException e) {
    System.out.println("Error: La entrada '" + entrada + "' no es un número válido");
}

Rendimiento y eficiencia

Las operaciones de conversión, especialmente entre tipos primitivos y objetos, tienen un costo en rendimiento:

  • El autoboxing/unboxing excesivo puede afectar al rendimiento, especialmente en bucles.
  • La concatenación de cadenas con + crea nuevos objetos String; usa StringBuilder para concatenaciones extensas.
  • La conversión entre tipos numéricos puede causar cálculos adicionales.
// Ejemplo de mejora de rendimiento en concatenaciones
public class EficienciaConcatenacion {
    public static void main(String[] args) {
        long inicio, fin;
        
        // Concatenación ineficiente
        inicio = System.currentTimeMillis();
        String resultado1 = "";
        for (int i = 0; i < 100000; i++) {
            resultado1 += i;  // Crea un nuevo String en cada iteración
        }
        fin = System.currentTimeMillis();
        System.out.println("Tiempo con += : " + (fin - inicio) + " ms");
        
        // Concatenación eficiente
        inicio = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 100000; i++) {
            sb.append(i);
        }
        String resultado2 = sb.toString();
        fin = System.currentTimeMillis();
        System.out.println("Tiempo con StringBuilder: " + (fin - inicio) + " ms");
    }
}

Ejemplos prácticos

Ejemplo 1: Calculadora con entrada de usuario

import java.util.Scanner;

public class CalculadoraConConversiones {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("Calculadora Simple");
        System.out.println("------------------");
        
        try {
            System.out.print("Introduce el primer número: ");
            String entrada1 = scanner.nextLine();
            double numero1 = Double.parseDouble(entrada1);
            
            System.out.print("Introduce el segundo número: ");
            String entrada2 = scanner.nextLine();
            double numero2 = Double.parseDouble(entrada2);
            
            System.out.println("\nResultados:");
            System.out.println("Suma: " + (numero1 + numero2));
            System.out.println("Resta: " + (numero1 - numero2));
            System.out.println("Multiplicación: " + (numero1 * numero2));
            
            if (numero2 != 0) {
                System.out.println("División: " + (numero1 / numero2));
            } else {
                System.out.println("División: No es posible dividir entre cero");
            }
            
            // Conversión a enteros
            System.out.println("\nConversión a enteros:");
            System.out.println("Primer número como entero: " + (int)numero1);
            System.out.println("Segundo número como entero: " + (int)numero2);
            
        } catch (NumberFormatException e) {
            System.out.println("Error: Entrada no válida. Debes ingresar números.");
        } finally {
            scanner.close();
        }
    }
}

Ejemplo 2: Procesamiento de datos de un archivo

Este ejemplo simula la lectura de datos de diferentes tipos desde un archivo:

public class ProcesadorDatos {
    public static void main(String[] args) {
        // Simulamos datos de un archivo CSV
        String[] datos = {
            "Juan,25,1.75,true",
            "María,30,1.65,false",
            "Carlos,22,1.80,true",
            "Ana,28,1.70,false",
            "inválido,edad,altura,activo"  // Línea con datos inválidos
        };
        
        // Procesamos cada línea
        for (String linea : datos) {
            try {
                String[] campos = linea.split(",");
                
                if (campos.length != 4) {
                    throw new Exception("Formato de línea incorrecto");
                }
                
                // Conversión de los datos
                String nombre = campos[0];
                int edad = Integer.parseInt(campos[1]);
                double altura = Double.parseDouble(campos[2]);
                boolean activo = Boolean.parseBoolean(campos[3]);
                
                // Cálculos con los datos convertidos
                int añoNacimiento = 2025 - edad;
                String estado = activo ? "Activo" : "Inactivo";
                
                // Mostramos resultados formateados
                System.out.println("Persona: " + nombre);
                System.out.println("  Edad: " + edad + " años (nacido/a en " + añoNacimiento + ")");
                System.out.println("  Altura: " + String.format("%.2f", altura) + " metros");
                System.out.println("  Estado: " + estado);
                System.out.println();
                
            } catch (NumberFormatException e) {
                System.out.println("Error al procesar la línea: " + linea);
                System.out.println("  Causa: Formato numérico inválido");
                System.out.println();
            } catch (Exception e) {
                System.out.println("Error al procesar la línea: " + linea);
                System.out.println("  Causa: " + e.getMessage());
                System.out.println();
            }
        }
    }
}

Ejemplo 3: Conversión entre sistemas numéricos

import java.util.Scanner;

public class ConversionSistemasNumericos {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("Conversor de Sistemas Numéricos");
        System.out.println("-------------------------------");
        System.out.println("1. Decimal a otros sistemas");
        System.out.println("2. Binario a decimal");
        System.out.println("3. Hexadecimal a decimal");
        System.out.println("4. Octal a decimal");
        System.out.print("\nSelecciona una opción (1-4): ");
        
        try {
            int opcion = Integer.parseInt(scanner.nextLine());
            
            switch (opcion) {
                case 1:
                    System.out.print("Introduce un número decimal: ");
                    int decimal = Integer.parseInt(scanner.nextLine());
                    
                    System.out.println("\nConversiones:");
                    System.out.println("Binario: " + Integer.toBinaryString(decimal));
                    System.out.println("Octal: " + Integer.toOctalString(decimal));
                    System.out.println("Hexadecimal: " + Integer.toHexString(decimal).toUpperCase());
                    break;
                    
                case 2:
                    System.out.print("Introduce un número binario: ");
                    String binario = scanner.nextLine();
                    int valorBinario = Integer.parseInt(binario, 2);
                    
                    System.out.println("\nValor decimal: " + valorBinario);
                    break;
                    
                case 3:
                    System.out.print("Introduce un número hexadecimal: ");
                    String hexadecimal = scanner.nextLine();
                    int valorHex = Integer.parseInt(hexadecimal, 16);
                    
                    System.out.println("\nValor decimal: " + valorHex);
                    break;
                    
                case 4:
                    System.out.print("Introduce un número octal: ");
                    String octal = scanner.nextLine();
                    int valorOctal = Integer.parseInt(octal, 8);
                    
                    System.out.println("\nValor decimal: " + valorOctal);
                    break;
                    
                default:
                    System.out.println("Opción no válida");
            }
            
        } catch (NumberFormatException e) {
            System.out.println("Error: Entrada numérica no válida");
        } finally {
            scanner.close();
        }
    }
}

Resumen

La conversión de tipos en Java es una operación fundamental que nos permite trabajar con diferentes tipos de datos según nuestras necesidades. Hemos visto que existen dos tipos principales de conversiones: implícitas (automáticas) y explícitas (casting). Las conversiones implícitas ocurren cuando convertimos de un tipo "menor" a uno "mayor", mientras que las explícitas son necesarias en el caso contrario o cuando podría haber pérdida de información.

También hemos, también conocida como promoción de tipo, ocurre cuando Java convierte automáticamente un tipo de dato a otro sin necesidad de una intervención explícita. Esto sucede cuando:

  1. Los dos tipos son compatibles
  2. El tipo de destino es más grande que el tipo de origen

La conversión implícita