Tipo primitivo 'corto' - conversión en Java

78

Tengo una pregunta sobre el tipo primitivo shorten Java. Estoy usando JDK 1.6.

Si tengo lo siguiente:

short a = 2;
short b = 3;
short c = a + b;

el compilador no quiere compilar, dice que "no puede convertir de int a short" y sugiere que haga una conversión a short, así que esto:

short c = (short) (a + b);

realmente funciona. Pero mi pregunta es ¿por qué necesito lanzar? Los valores de ayb están en el rango de short- el rango de valores cortos es {-32,768, 32767}. También necesito transmitir cuando quiero realizar las operaciones -, *, / (no he comprobado si hay otros).

Si hago lo mismo para el tipo primitivo int, no necesito lanzar aa + bb a int. Lo siguiente funciona bien:

int aa = 2;
int bb = 3;
int cc = aa +bb;

Descubrí esto mientras diseñaba una clase en la que necesitaba agregar dos variables de tipo corto, y el compilador quería que hiciera una conversión. Si hago esto con dos variables de tipo int, no necesito lanzar.

Un pequeño comentario: lo mismo ocurre también con el tipo primitivo byte. Entonces, esto funciona:

byte a = 2;
byte b = 3;
byte c = (byte) (a + b);

pero esto no:

byte a = 2;
byte b = 3;
byte c = a + b;

Para long, float, double, y int, no hay necesidad de yeso. Solo por shorty bytevalores.

user42155
fuente

Respuestas:

64

Como se explica en breve C # (pero también para otros compiladores de lenguajes, como Java)

Hay una conversión implícita predefinida de short a int, long, float, double o decimal.

No puede convertir implícitamente tipos numéricos no literales de tamaño de almacenamiento más grande en cortos (consulte la Tabla de tipos integrales para conocer los tamaños de almacenamiento de los tipos integrales). Considere, por ejemplo, las siguientes dos variables cortas xey:

short x = 5, y = 12;

La siguiente declaración de asignación producirá un error de compilación, porque la expresión aritmética en el lado derecho del operador de asignación se evalúa como int por defecto.

short z = x + y;   // Error: no conversion from int to short

Para solucionar este problema, use un yeso:

short z = (short)(x + y);   // OK: explicit conversion

Sin embargo, es posible utilizar las siguientes declaraciones, donde la variable de destino tiene el mismo tamaño de almacenamiento o un tamaño de almacenamiento mayor:

int m = x + y;
long n = x + y;

Una buena pregunta de seguimiento es:

"¿Por qué la expresión aritmética en el lado derecho del operador de asignación se evalúa como int por defecto"?

Una primera respuesta se puede encontrar en:

Clasificación y verificación formal de plegado constante de enteros

La especificación del lenguaje Java define exactamente cómo se representan los números enteros y cómo se evaluarán las expresiones aritméticas de enteros . Esta es una propiedad importante de Java, ya que este lenguaje de programación ha sido diseñado para ser utilizado en aplicaciones distribuidas en Internet. Se requiere un programa Java para producir el mismo resultado independientemente de la máquina de destino que lo ejecute .

Por el contrario, C (y la mayoría de los lenguajes de programación imperativos y orientados a objetos ampliamente utilizados) es más descuidado y deja abiertas muchas características importantes. La intención detrás de esta especificación de lenguaje inexacta es clara. Se supone que los mismos programas en C se ejecutan en una arquitectura de 16 bits, 32 bits o incluso 64 bits al crear instancias de la aritmética entera de los programas fuente con las operaciones aritméticas integradas en el procesador de destino. Esto conduce a un código mucho más eficiente porque puede usar las operaciones de la máquina disponibles directamente. Siempre que los cálculos de números enteros se ocupen únicamente de que los números sean "suficientemente pequeños", no surgirán inconsistencias.

En este sentido, la aritmética de enteros en C es un marcador de posición que no está definido exactamente por la especificación del lenguaje de programación, sino que solo se instancia completamente al determinar la máquina de destino.

Java define con precisión cómo se representan los enteros y cómo se calcula la aritmética de enteros.

      Java Integers
--------------------------
Signed         |  Unsigned
--------------------------
long  (64-bit) |
int   (32-bit) |
short (16-bit) |  char (16-bit)
byte  (8-bit)  |

Char es el único tipo de entero sin signo. Sus valores representan caracteres Unicode, de \u0000a \uffff, es decir, de 0 a 2 16 −1.

Si un operador entero tiene un operando de tipo long, entonces el otro operando también se convierte a tipo long. De lo contrario, la operación se realiza en operandos de tipo int, si es necesario, los operandos más cortos se convierten en int . Las reglas de conversión se especifican exactamente.

[De Electronic Notes in Theoretical Computer Science 82 No. 2 (2003)
Blesner-Blech-COCV 2003: Sabine GLESNER , Jan Olaf BLECH,
Fakultät für Informatik,
Universität Karlsruhe
Karlsruhe, Alemania]

VonC
fuente
También T a, b; a += bes equivalente a T a, b; a = (T) (a + b): observe la conversión agregada por el compilador.
wchargin
17

EDITAR: Bien, ahora sabemos que es Java ...

La sección 4.2.2 de la especificación del lenguaje Java establece:

El lenguaje de programación Java proporciona una serie de operadores que actúan sobre valores integrales:

[...]

  • Los operadores numéricos, que dan como resultado un valor de tipo int o long:
  • [...]
  • Los operadores aditivos + y - (§15.18)

  • En otras palabras, es como C #: el operador de suma (cuando se aplica a tipos integrales) solo da como resultado into long, por lo que necesita convertir para asignar a una shortvariable.

    Respuesta original (C #)

    En C # (no ha especificado el idioma, supongo), los únicos operadores de suma en tipos primitivos son:

    int operator +(int x, int y);
    uint operator +(uint x, uint y);
    long operator +(long x, long y);
    ulong operator +(ulong x, ulong y);
    float operator +(float x, float y);
    double operator +(double x, double y);
    

    Estos se encuentran en la especificación C # 3.0, sección 7.7.4. Además, se define la suma decimal:

    decimal operator +(decimal x, decimal y);
    

    (La suma de enumeración, la concatenación de cadenas y la combinación de delegados también se definen allí).

    Como puede ver, no hay short operator +(short x, short y)operador, por lo que ambos operandos se convierten implícitamente a int y se usa la forma int. Eso significa que el resultado es una expresión de tipo "int", de ahí la necesidad de emitir.

    Jon Skeet
    fuente
    Puede agregar msdn.microsoft.com/en-us/library/aa691375(VS.71).aspx para vincular a la sección 7.7.4
    VonC
    Sí, es una pena que no exista una versión simple con hipervínculo de la especificación C # 3.0. La versión de MSDN es demasiado dolorosa en mi opinión :(
    Jon Skeet
    Ahora sabemos que es Java, por lo tanto, no hay uint ni ulong. No recuerdo si Java sobrecarga el operador + para BigInteger y / o BigDecimal
    finnw
    ¿Todavía en 0? Vamos ... +1, mencionaste las especificaciones correctamente;) ¿Podrías echar un vistazo a mi pregunta de seguimiento en mi respuesta y ver si tienes alguna idea sobre ese tema? (Es decir, "¿por qué int por defecto? ")
    VonC
    la URL ha sido orracleificada. docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.2
    BaroqueBobcat
    16

    En C # y Java, la expresión aritmática del lado derecho de la asignación se evalúa como int de forma predeterminada. Es por eso que necesita volver a un short, porque no hay una conversión implícita de int a short, por razones obvias.

    Rik
    fuente
    8

    Dado que la pregunta "por qué int por defecto" no ha sido respondida ...

    Primero, "predeterminado" no es realmente el término correcto (aunque lo suficientemente cercano). Como señaló VonC, una expresión compuesta de ints y longs tendrá un resultado largo. Y una operación que consta de entradas / registros y dobles tendrá un resultado doble. El compilador promueve los términos de una expresión a cualquier tipo que proporcione un mayor rango y / o precisión en el resultado (se supone que los tipos de punto flotante tienen mayor rango y precisión que la integral, aunque se pierde precisión al convertir largos largos en dobles).

    Una advertencia es que esta promoción solo se aplica a los términos que la necesitan. Entonces, en el siguiente ejemplo, la subexpresión 5/4 usa solo valores integrales y se realiza usando matemáticas enteras, aunque la expresión general involucra un doble. El resultado no es el que cabría esperar ...

    (5/4) * 1000.0
    

    Bien, entonces, ¿por qué byte y short se promueven a int? Sin ninguna referencia que me respalde, se debe a la practicidad: hay un número limitado de códigos de bytes.

    "Bytecode", como su nombre lo indica, utiliza un solo byte para especificar una operación. Por ejemplo iadd , que suma dos ints. Actualmente, se definen 205 códigos de operación y las matemáticas de números enteros toman 18 para cada tipo (es decir, 36 en total entre números enteros y largos), sin contar los operadores de conversión.

    Si es corto, y cada byte tiene su propio conjunto de códigos de operación, estaría en 241, lo que limita la capacidad de expansión de la JVM. Como dije, no hay referencias que me respalden en esto, pero sospecho que Gosling et al dijeron "¿con qué frecuencia la gente usa realmente pantalones cortos?" Por otro lado, la promoción de bytes a int conduce a este efecto no tan maravilloso (la respuesta esperada es 96, la real es -16):

    byte x = (byte)0xC0;
    System.out.println(x >> 2);
    
    kdgregory
    fuente
    La respuesta esperada es 48, ¿no?
    mafu
    @mafu Lo es. Así que >>lanza a int?? El efecto, sin embargo, es lo que en Z80 se llama SRA(desplazamiento aritmético a la derecha), que desplaza bits de un byte al lugar 1 de la derecha, perdiendo el que está más a la derecha y duplicando el que está más a la izquierda (dividiendo así un byte con signo por 2), en lugar de SRL(desplazamiento lógico a la derecha), que deja un bit cero a la izquierda (lo mismo que dividir un byte sin signo por 2), que es en lo que se basa la "respuesta esperada".
    Heimdall
    5

    Qué idioma estás usando?

    Muchos lenguajes basados ​​en C tienen la regla de que cualquier expresión matemática se realiza en tamaño int o mayor. Debido a esto, una vez que agrega dos cortos, el resultado es de tipo int. Esto provoca la necesidad de un yeso.

    Darron
    fuente
    2

    Java siempre utiliza al menos valores de 32 bits para los cálculos. Esto se debe a la arquitectura de 32 bits que era común en 1995 cuando se introdujo Java. El tamaño del registro en la CPU era de 32 bits y la unidad aritmética lógica aceptaba 2 números de la longitud de un registro de la CPU. Entonces, los cpus se optimizaron para tales valores.

    Esta es la razón por la que todos los tipos de datos que admiten operaciones aritméticas y tienen menos de 32 bits se convierten a int (32 bits) tan pronto como los usa para los cálculos.

    Entonces, en resumen, se debió principalmente a problemas de rendimiento y hoy en día se mantiene por compatibilidad.

    Andreas Mennel
    fuente
    1

    En java, cada expresión numérica como:

    anyPrimitive zas = 1;
    anyPrimitive bar = 3;
    ?? x = zas  + bar 
    

    x siempre resultará ser al menos un int, o un long si uno de los elementos de adición fue un long.

    Pero hay algunas peculiaridades difíciles

    byte a = 1; // 1 is an int, but it won't compile if you use a variable
    a += 2; // the shortcut works even when 2 is an int
    a++; // the post and pre increment operator work
    
    Jaime Hablutzel
    fuente
    1

    AFAIS, nadie menciona el finaluso de eso. Si modifica su último ejemplo y define las variables ayb como final variables, entonces el compilador tiene la seguridad de que su suma, valor 5, puede asignarse a una variable de tipo byte, sin pérdida de precisión. En este caso, el compilador es bueno para asignar la suma de ayb a c. Aquí está el código modificado:

    final byte a = 2;
    final byte b = 3;
    byte c = a + b;
    
    snr
    fuente
    No exactamente. final byte a = (byte) (new Random().nextInt(4));y Incompatible typesasoma de nuevo su fea cabeza. No es solo finalidad, es poder compilarlo a un valor que se ajuste al tipo.
    LAFK dice Reincorporar a Monica
    cuéntele su queja a Herbert Schildt, es su idea. @LIttleAncientForestKami
    snr
    Lo haría si tuviera una @snr, especialmente porque ni siquiera Shildt podría ayudar contra javac. ;-) Tus palabras implican que el final compilador asegura , y puede que no sea suficiente. Si tomamos su ejemplo palabra por palabra, todo está bien. Pero intente reemplazarlo b = 3con b = (byte)(new Random().nextInt(4)). y los tipos incompatibles están de vuelta y a + b necesita conversión nuevamente. Es posible que desee agregar eso a su respuesta.
    LAFK dice Reincorporar a Monica
    1

    Cualquier tipo de datos que sea menor que "int" (excepto booleano) se convierte implícitamente en "int".

    En tu caso:

    short a = 2;
    short b = 3;
    short c = a + b;
    

    El resultado de (a + b) se convierte implícitamente en un int. Y ahora lo estás asignando a "corto". De modo que obtienes el error.

    short, byte, char - para todos estos obtendremos el mismo error.

    Rushikesh Garadade
    fuente
    0

    Me gustaría agregar algo que no se ha señalado. Java no tiene en cuenta los valores que le ha dado a las variables (2 y 3) en ...

    corto a = 2; corto b = 3; corto c = a + b;

    Por lo que Java sabe, podrías hacer esto ...

    corto a = 32767; corto b = 32767; corto c = a + b;

    Lo cual estaría fuera del rango de short, automáticamente pone el resultado en un int porque es "posible" que el resultado sea más que un short pero no más que un int. Se eligió Int como "predeterminado" porque, básicamente, la mayoría de las personas no codificarán valores por encima de 2,147,483,647 o por debajo de -2,147,483,648


    fuente
    Tonterías, porque puedo agregar dos int MAXVALUE y no requerirá que LHS sea largo.
    Gordon