Примітивний тип 'короткий' - кастинг на Java


78

У мене питання про примітивний тип shortу Java. Я використовую JDK 1.6.

Якщо у мене є таке:

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

компілятор не хоче компілювати - він каже, що він "не може перетворити з int на короткий" і пропонує мені зробити привід short, тому це:

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

дійсно працює. Але моє питання полягає в тому, чому мені потрібно робити кастинг? Значення a та b знаходяться в діапазоні short- діапазон коротких значень становить {-32,768, 32767}. Мені також потрібно робити кастинг, коли я хочу виконати операції -, *, / (я не перевіряв інші).

Якщо я роблю те саме для примітивного типу int, мені не потрібно додавати aa + bb до int. Наступне чудово працює:

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

Я виявив це під час проектування класу, де мені потрібно було додати дві змінні типу short, і компілятор хотів, щоб я зробив закид. Якщо я роблю це з двома змінними типу int, мені не потрібно робити кастинг.

Невелике зауваження: те саме відбувається і з примітивним типом byte. Отже, це працює:

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

але це не так:

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

Для long, float, doubleі intнемає ніякої необхідності в гіпс. Тільки для shortі byteзначення.

Відповіді:


64

Як пояснюється коротко C # (але також для інших мовних компіляторів, таких як Java)

Існує заздалегідь визначене неявне перетворення з короткого на int, long, float, double або decimal.

Ви не можете неявно перетворити нелітеральні числові типи великого обсягу сховища в короткі (див. Таблицю інтегральних типів для розмірів сховищ цілісних типів). Розглянемо, наприклад, наступні дві короткі змінні x та y:

short x = 5, y = 12;

Наступний оператор присвоєння видасть помилку компіляції, оскільки арифметичний вираз праворуч від оператора присвоєння за замовчуванням має значення int.

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

Щоб вирішити цю проблему, використовуйте привід:

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

Можна, однак, використовувати наступні твердження, коли цільова змінна має однаковий або більший розмір сховища:

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

Хорошим подальшим запитанням є:

"чому арифметичний вираз у правій частині оператора присвоєння за замовчуванням оцінює як int"?

Першу відповідь можна знайти в:

Класифікація та формальна перевірка цілого цілого постійного згортання

Специфікація мови Java визначає, як саме представляються цілі числа та як обчислюються цілочисельні арифметичні вирази . Це важлива властивість Java, оскільки ця мова програмування розроблена для використання в розподілених додатках в Інтернеті. Програма Java потрібна для отримання того самого результату незалежно від цільової машини, яка його виконує .

На відміну від цього, C (і більшість широко використовуваних імперативних та об'єктно-орієнтованих мов програмування) є більш недбалим і залишає відкритими багато важливих характеристик. Намір цієї неточної мовної специфікації зрозумілий. Ті самі програми C повинні працювати на 16-розрядної, 32-розрядної або навіть 64-розрядної архітектурі шляхом створення цілочисельної арифметики вихідних програм з арифметичними операціями, вбудованими в цільовий процесор. Це призводить до набагато ефективнішого коду, оскільки він може безпосередньо використовувати доступні операції машини. Поки цілочисельні обчислення мають справу лише з «достатньо малими числами», ніяких невідповідностей не виникне.

У цьому сенсі цілочисельна арифметика C - це заповнювач, який точно не визначений специфікацією мови програмування, а лише повністю створений за допомогою визначення цільової машини.

Java точно визначає, як представляються цілі числа і як обчислюється цілочисельна арифметика.

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

Char - єдиний цілочисельний тип без знака. Його значення представляють символи Unicode, від \u0000до \uffff, тобто від 0 до 2 16 -1.

Якщо цілочисельний оператор має операнд типу long, тоді інший операнд також перетворюється в тип long. В іншому випадку операція виконується над операндами типу int, при необхідності коротші операнди перетворюються в int . Правила перетворення точно вказані.

[З електронних заміток у теоретичній комп’ютерній науці 82 № 2 (2003)
Blesner-Blech-COCV 2003: Сабіне ГЛЕСНЕР , Ян Олаф БЛЕХ,
Факультет інформатики,
Університет Карлсруе,
Карлсруе, Німеччина]


Також T a, b; a += bеквівалентно T a, b; a = (T) (a + b): зверніть увагу на доданий компілятором склад.
wchargin

17

EDIT: Гаразд, тепер ми знаємо, що це Java ...

У розділі 4.2.2 Специфікації мови Java зазначено:

Мова програмування Java надає ряд операторів, які діють на інтегральні значення:

[...]

  • Числові оператори, результатом яких є значення типу int або long:
  • [...]
  • Адитивні оператори + та - (§15.18)

  • Іншими словами, це як C # - оператор додавання (при застосуванні до інтегральних типів) лише коли-небудь призводить до intабо long, саме тому вам потрібно привести, щоб призначити shortзмінну.

    Оригінальна відповідь (C #)

    У C # (ви не вказали мову, тому я здогадуюсь), єдиними операторами додавання для примітивних типів є:

    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);
    

    Це в специфікації C # 3.0, розділ 7.7.4. Крім того, визначається десяткове додавання:

    decimal operator +(decimal x, decimal y);
    

    (Там також визначено додавання переліку, об'єднання рядків та комбінацію делегатів.)

    Як бачите, short operator +(short x, short y)оператора немає - тому обидва операнди неявно перетворюються на int, і використовується форма int. Це означає, що результат є вираженням типу "int", отже, необхідність приведення.


    Ви можете додати msdn.microsoft.com/en-us/library/aa691375(VS.71).aspx для посилання на розділ 7.7.4
    VonC

    Так, прикро, що не існує простої версії специфікації C # 3.0 з гіперпосиланням. Версія MSDN є занадто болючою IMO :(
    Джон Скіт,

    Зараз ми знаємо, що це Java, отже, немає uint або ulong. Я не пам'ятаю, чи перевантажує Java оператор + для BigInteger та / або BigDecimal
    finnw

    Все ще на 0?! Давай ... +1, ти правильно згадав специфікації;) Чи не міг би ти поглянути на моє подальше запитання в моїй відповіді та побачити, чи маєш ти певне розуміння цієї теми? (Тобто "чому int за замовчуванням? ")
    VonC

    URL-адреса була оракульована. docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.2
    BaroqueBobcat

    16

    У C # та Java арифматичний вираз у правій частині призначення за замовчуванням має значення int. Ось чому вам потрібно повернутися до короткого, оскільки не існує неявної форми перетворення int у short, із зрозумілих причин.


    8

    Враховуючи, що на запитання "чому int за замовчуванням" не відповіли ...

    По-перше, "за замовчуванням" насправді не є правильним терміном (хоча і досить близьким). Як зазначає VonC, вираз, що складається з ints і longs, матиме довгий результат. І операція, що складається з ints / logs та double, матиме подвійний результат. Компілятор просуває терміни виразу до будь-якого типу, що забезпечує більший діапазон та / або точність результату (передбачається, що типи з плаваючою комою мають більший діапазон і точність, ніж інтеграл, хоча ви втрачаєте точність, перетворюючи великі довжини у подвійні).

    Одне застереження полягає в тому, що це просування відбувається лише на умовах, які цього потребують. Отже, у наступному прикладі підвираз 5/4 використовує лише інтегральні значення і виконується з використанням цілочисельної математики, хоча загальний вираз передбачає подвійне. Результат не такий, як можна очікувати ...

    (5/4) * 1000.0
    

    Добре, так чому байт і шорт підвищуються до int? Без будь-яких посилань на підтримку, це пов’язано з практичністю: існує обмежена кількість байт-кодів.

    "Байт-код", як випливає з назви, використовує один байт для вказівки операції. Наприклад iadd , який додає два ints. В даний час визначено 205 операційних кодів , і цілочисельна математика займає 18 для кожного типу (тобто 36 загалом між цілими та довгими), не враховуючи операторів перетворення.

    Якщо короткий і байт кожен отримає свій власний набір кодів операцій, ви знаходитесь на рівні 241, обмежуючи можливість JVM розширюватися. Як я вже говорив, жодних посилань на це не підтримує, але я підозрюю, що Гослінг та ін. Сказали: "як часто люди насправді використовують шорти?" З іншого боку, просування байта до int призводить до цього не дуже чудового ефекту (очікувана відповідь 96, фактична -16):

    byte x = (byte)0xC0;
    System.out.println(x >> 2);
    

    Очікувана відповідь - 48, чи не так?
    мафу

    @mafu Це так. Так >>кидає на int?? Ефектом, однак, є те, що називається Z80 SRA(арифметичне зсув вправо), яке зміщує біти байта вправо на 1 місце, втрачаючи крайнє право і дублюючи крайнє ліве (таким чином, розділивши підписаний байт на 2), на відміну від SRL(логічний зсув вправо), який залишає нульовий біт ліворуч (те саме, що поділити непідписаний байт на 2), на чому заснована "очікувана відповідь".
    Heimdall

    5

    Якою мовою ви користуєтесь?

    У багатьох мовах, заснованих на мові С, існує правило, згідно з яким будь-який математичний вираз виконується розміром int або більше. Через це, як тільки ви додасте два шорти, результат буде типу int. Це викликає необхідність у гіпсі.


    2

    Java завжди використовує принаймні 32 бітові значення для обчислень. Це пов’язано з 32-розрядною архітектурою, яка була поширеною в 1995 році, коли була представлена ​​Java. Розмір реєстру в центральному процесорі становив 32 біти, і арифметичний логічний блок приймав 2 числа довжини регістру процесора. Тож процесор був оптимізований для таких значень.

    Це є причиною того, що всі типи даних, які підтримують арифметичні операції і мають менше 32 біт, перетворюються на int (32 біт), як тільки ви використовуєте їх для обчислень.

    Отже, підсумовуючи це, головним чином, це було пов’язано з проблемами продуктивності, і сьогодні воно зберігається для сумісності.


    1

    У Java кожен числовий вираз, такий як:

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

    x завжди буде мати принаймні int або long, якщо один із елементів додавання був long.

    Але є кілька примх жорстких

    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
    

    1

    AFAIS, про finalвикористання цього ніхто не згадує . Якщо ви модифікуєте свій останній приклад і визначаєте змінні a та b як final змінні, то компілятор гарантує, що їх сума, значення 5, може бути присвоєна змінній типу byte, без втрати точності. У цьому випадку компілятору добре призначити суму a і b c. Ось модифікований код:

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

    Не зовсім. final byte a = (byte) (new Random().nextInt(4));і знову Incompatible typesвиховує свою потворну голову. Це не просто остаточність, це можливість компілювати її до значення, яке відповідає типу.
    LAFK каже "Поновити Моніку"

    повідомте свою скаргу Герберту Шильдту, це його ідея. @LIttleAncientForestKami
    ЗСШ

    Я б, якби мав один @snr, тим більше, що навіть Шилдт не міг допомогти проти javac. ;-) Ваші слова натякають на те, що final запевняє компілятор , і цього може бути недостатньо. Якщо взяти ваш приклад дослівно, все гаразд і чудово. Але спробуйте замінити b = 3на b = (byte)(new Random().nextInt(4)). і несумісні типи повертаються, і a + b знову потребує кастингу. Можливо, ви захочете додати це до своєї відповіді.
    LAFK каже “Поновити Моніку”

    1

    Будь-який тип даних, який нижчий за "int" (крім булевого), неявно перетворюється на "int".

    У вашому випадку:

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

    Результат (a + b) неявно перетворюється на int. І тепер ви присвоюєте йому "короткий". Отже, ви отримуєте помилку.

    short, byte, char - для всіх них ми отримаємо однакову помилку.


    0

    Я хотів би додати те, на що не вказували. Java не враховує значення, які ви надали змінним (2 і 3) у ...

    коротке a = 2; короткий b = 3; короткий c = a + b;

    Отже, наскільки відомо Java, ви могли б це зробити ...

    коротке a = 32767; короткий b = 32767; короткий c = a + b;

    Що було б поза діапазоном короткого, він автоматично упаковує результат до int, оскільки "можливо", що результат буде більше, ніж короткий, але не більше, ніж int. Int було вибрано як "за замовчуванням", оскільки в основному більшість людей не мають жорстких значень кодування вище 2 147 483 647 або нижче -2 147 483 648


    Нісенітниця, тому що я можу додати два int MAXVALUE, і це не вимагатиме, щоб LHS був довгим.
    Гордон,
    Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
    Licensed under cc by-sa 3.0 with attribution required.