Primitiver Typ 'Short' - Casting in Java

78

Ich habe eine Frage zum primitiven Typ shortin Java. Ich benutze JDK 1.6.

Wenn ich folgendes habe:

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

Der Compiler möchte nicht kompilieren - er sagt, dass er "nicht von int nach short konvertieren kann" und schlägt vor, dass ich eine Besetzung mache short, also:

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

funktioniert wirklich. Aber meine Frage ist, warum ich gießen muss? Die Werte von a und b liegen im Bereich von short- der Bereich der Kurzwerte beträgt {-32.768, 32767}. Ich muss auch wirken, wenn ich die Operationen ausführen möchte -, *, / (ich habe nicht nach anderen gesucht).

Wenn ich dasselbe für primitive Typen intmache, muss ich aa + bb nicht umwandeln int. Folgendes funktioniert gut:

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

Ich habe dies beim Entwerfen einer Klasse entdeckt, in der ich zwei Variablen vom Typ short hinzufügen musste, und der Compiler wollte, dass ich eine Besetzung mache. Wenn ich dies mit zwei Variablen vom Typ intmache, muss ich nicht umwandeln.

Eine kleine Bemerkung: Das gleiche passiert auch mit dem primitiven Typ byte. Das funktioniert also:

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

aber das nicht:

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

Für long, float, double, und intgibt es keine Notwendigkeit, gegossen. Nur für shortund byteWerte.

user42155
quelle

Antworten:

64

Wie in kurz C # erklärt (aber auch für andere Sprachcompiler wie Java)

Es gibt eine vordefinierte implizite Konvertierung von kurz nach int, long, float, double oder decimal.

Sie können nichtliterale numerische Typen mit größerer Speichergröße nicht implizit in kurze konvertieren (die Speichergrößen für integrale Typen finden Sie in der Tabelle der integralen Typen). Betrachten Sie zum Beispiel die folgenden zwei kurzen Variablen x und y:

short x = 5, y = 12;

Die folgende Zuweisungsanweisung führt zu einem Kompilierungsfehler, da der arithmetische Ausdruck auf der rechten Seite des Zuweisungsoperators standardmäßig int ergibt.

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

Verwenden Sie eine Besetzung, um dieses Problem zu beheben:

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

Es ist jedoch möglich, die folgenden Anweisungen zu verwenden, wenn die Zielvariable dieselbe Speichergröße oder eine größere Speichergröße hat:

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

Eine gute Anschlussfrage lautet:

"Warum wird der arithmetische Ausdruck auf der rechten Seite des Zuweisungsoperators standardmäßig mit int ausgewertet?"

Eine erste Antwort finden Sie in:

Klassifizieren und formales Überprüfen der ganzzahligen konstanten Faltung

Die Java-Sprachspezifikation definiert genau, wie Ganzzahlnummern dargestellt werden und wie Ganzzahlarithmetikausdrücke ausgewertet werden sollen . Dies ist eine wichtige Eigenschaft von Java, da diese Programmiersprache für die Verwendung in verteilten Anwendungen im Internet entwickelt wurde. Ein Java-Programm ist erforderlich, um unabhängig von der Zielmaschine, die es ausführt, dasselbe Ergebnis zu erzielen .

Im Gegensatz dazu ist C (und die Mehrheit der weit verbreiteten imperativen und objektorientierten Programmiersprachen) schlampiger und lässt viele wichtige Merkmale offen. Die Absicht hinter dieser ungenauen Sprachspezifikation ist klar. Dieselben C-Programme sollen auf einer 16-Bit-, 32-Bit- oder sogar 64-Bit-Architektur ausgeführt werden, indem die ganzzahlige Arithmetik der Quellprogramme mit den im Zielprozessor integrierten arithmetischen Operationen instanziiert wird. Dies führt zu einem viel effizienteren Code, da die verfügbaren Maschinenoperationen direkt verwendet werden können. Solange die Ganzzahlberechnungen nur Zahlen behandeln, die "ausreichend klein" sind, treten keine Inkonsistenzen auf.

In diesem Sinne ist die C-Integer-Arithmetik ein Platzhalter, der nicht genau durch die Programmiersprachenspezifikation definiert ist, sondern nur durch Bestimmen der Zielmaschine vollständig instanziiert wird.

Java definiert genau, wie Ganzzahlen dargestellt werden und wie Ganzzahlarithmetik berechnet werden soll.

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

Char ist der einzige vorzeichenlose Integer-Typ. Seine Werte repräsentieren Unicode-Zeichen von \u0000bis \uffff, dh von 0 bis 2 16 −1.

Wenn ein Ganzzahloperator einen Operanden vom Typ long hat, wird der andere Operand ebenfalls in den Typ long konvertiert. Andernfalls wird die Operation für Operanden vom Typ int ausgeführt. Falls erforderlich, werden kürzere Operanden in int konvertiert . Die Konvertierungsregeln sind genau festgelegt.

[Aus elektronischen Notizen in der Theoretischen Informatik 82 Nr. 2 (2003)
Blesner-Blech-COCV 2003: Sabine GLESNER , Jan Olaf BLECH,
Fakultät für Informatik,
Universität Karlsruhe
Karlsruhe, Deutschland]

VonC
quelle
Auch T a, b; a += bentspricht T a, b; a = (T) (a + b): Bekanntmachung der Compiler-added gegossen.
Wchargin
17

EDIT: Okay, jetzt wissen wir, dass es Java ist ...

In Abschnitt 4.2.2 der Java-Sprachspezifikation heißt es:

Die Programmiersprache Java bietet eine Reihe von Operatoren, die auf ganzzahlige Werte einwirken:

[...]

  • Die numerischen Operatoren, die zu einem Wert vom Typ int oder long führen:
  • [...]
  • Die additiven Operatoren + und - (§15.18)

  • Mit anderen Worten, es ist wie bei C # - der Additionsoperator (wenn er auf integrale Typen angewendet wird) führt immer nur zu intoder long, weshalb Sie umwandeln müssen, um einer shortVariablen zuzuweisen .

    Ursprüngliche Antwort (C #)

    In C # (Sie haben die Sprache nicht angegeben, also schätze ich) sind die einzigen Additionsoperatoren für primitive Typen:

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

    Diese befinden sich in der C # 3.0-Spezifikation, Abschnitt 7.7.4. Zusätzlich wird die Dezimaladdition definiert:

    decimal operator +(decimal x, decimal y);
    

    (Dort werden auch das Hinzufügen von Aufzählungen, die Verkettung von Zeichenfolgen und die Kombination von Delegaten definiert.)

    Wie Sie sehen, gibt es keinen short operator +(short x, short y)Operator. Daher werden beide Operanden implizit in int konvertiert und die int-Form wird verwendet. Das heißt, das Ergebnis ist ein Ausdruck vom Typ "int", daher muss gegossen werden.

    Jon Skeet
    quelle
    Sie können msdn.microsoft.com/en-us/library/aa691375(VS.71).aspx hinzufügen , um einen Link zum Abschnitt
    7.7.4
    Ja, es ist eine Schande, dass es keine einfache Version der C # 3.0-Spezifikation mit Hyperlink gibt . Die MSDN-Version ist zu schmerzhaft IMO :(
    Jon Skeet
    Wir wissen jetzt, dass es Java ist, daher gibt es kein Uint oder Ulong. Ich kann mich nicht erinnern, ob Java Operator + für BigInteger und / oder BigDecimal
    überlastet
    Immer noch bei 0 ?! Komm schon ... +1, du hast die Spezifikationen richtig erwähnt;) Könntest du dir meine Folgefrage in meiner Antwort ansehen und sehen, ob du einen Einblick in dieses Thema hast? (Dh "warum int standardmäßig?" ")
    VonC
    Die URL wurde oracleifiziert. docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.2
    BaroqueBobcat
    16

    In C # und Java wird der arithmatische Ausdruck auf der rechten Seite der Zuweisung standardmäßig als int ausgewertet. Aus diesem Grund müssen Sie auf einen Short zurückgreifen, da es aus offensichtlichen Gründen keine implizite Konvertierungsform int to short gibt.

    Rik
    quelle
    8

    Da die Frage "Warum int standardmäßig" nicht beantwortet wurde ...

    Erstens ist "Standard" nicht wirklich der richtige Begriff (obwohl nah genug). Wie von VonC festgestellt, hat ein Ausdruck, der aus Ints und Longs besteht, ein langes Ergebnis. Und eine Operation, die aus Ints / Logs und Doubles besteht, hat ein doppeltes Ergebnis. Der Compiler stellt die Begriffe eines Ausdrucks auf einen beliebigen Typ her, der einen größeren Bereich und / oder eine größere Genauigkeit im Ergebnis bietet (Gleitkommatypen haben vermutlich einen größeren Bereich und eine größere Genauigkeit als Integrale, obwohl Sie die Genauigkeit verlieren, wenn Sie große Longs in Double konvertieren).

    Eine Einschränkung ist, dass diese Aktion nur zu den Bedingungen erfolgt, die sie benötigen. Im folgenden Beispiel verwendet der Unterausdruck 5/4 nur ganzzahlige Werte und wird mit ganzzahliger Mathematik ausgeführt, obwohl der Gesamtausdruck ein Doppel enthält. Das Ergebnis ist nicht das, was Sie erwarten könnten ...

    (5/4) * 1000.0
    

    OK, warum werden Byte und Short zu int heraufgestuft? Ohne Hinweise, die mich unterstützen, liegt es an der Praktikabilität: Es gibt eine begrenzte Anzahl von Bytecodes.

    "Bytecode" verwendet, wie der Name schon sagt, ein einzelnes Byte, um eine Operation anzugeben. Zum Beispiel iadd , das zwei Ints hinzufügt. Derzeit sind 205 Opcodes definiert , und für jeden Typ werden 18 Ganzzahlen berechnet (dh insgesamt 36 zwischen Ganzzahl und Lang), wobei die Konvertierungsoperatoren nicht berücksichtigt werden.

    Wenn kurz und Byte jeweils einen eigenen Satz von Opcodes haben, sind Sie bei 241, was die Erweiterungsfähigkeit der JVM einschränkt. Wie gesagt, keine Hinweise, die mich darauf stützen könnten, aber ich vermute, dass Gosling et al. "Wie oft benutzen die Leute tatsächlich Shorts?" Auf der anderen Seite führt das Heraufstufen von Byte zu int zu diesem nicht so wunderbaren Effekt (die erwartete Antwort ist 96, die tatsächliche ist -16):

    byte x = (byte)0xC0;
    System.out.println(x >> 2);
    
    kdgregory
    quelle
    Die erwartete Antwort ist 48, nicht wahr?
    Mafu
    @mafu Es ist. Also >>wirft zu int?? Der Effekt ist jedoch das, was in Z80 als SRA(Verschiebung nach rechts arithmetisch) bezeichnet wird, wodurch Bits eines Bytes um 1 Stelle nach rechts verschoben werden, wobei das am weitesten rechts stehende verloren geht und das am weitesten links stehende (wobei ein vorzeichenbehaftetes Byte durch 2 geteilt wird) dupliziert wird SRL(logisch nach rechts verschieben), wodurch links ein Nullbit verbleibt (wie beim Teilen eines vorzeichenlosen Bytes durch 2), worauf die "erwartete Antwort" basiert.
    Heimdall
    5

    Welche Sprache benutzt du?

    Viele C-basierte Sprachen haben die Regel, dass jeder mathematische Ausdruck in der Größe int oder größer ausgeführt wird. Aus diesem Grund ist das Ergebnis vom Typ int, sobald Sie zwei Kurzschlüsse hinzugefügt haben. Dies erfordert eine Besetzung.

    Darron
    quelle
    2

    Java verwendet für Berechnungen immer mindestens 32-Bit-Werte. Dies ist auf die 32-Bit-Architektur zurückzuführen, die 1995 bei der Einführung von Java üblich war. Die Registergröße in der CPU betrug 32 Bit und die arithmetische Logikeinheit akzeptierte 2 Zahlen der Länge eines CPU-Registers. Daher wurden die CPUs für solche Werte optimiert.

    Dies ist der Grund, warum alle Datentypen, die arithmetische Optionen unterstützen und weniger als 32 Bit haben, in int (32 Bit) konvertiert werden, sobald Sie sie für Berechnungen verwenden.

    Zusammenfassend war dies hauptsächlich auf Leistungsprobleme zurückzuführen und wird heutzutage aus Kompatibilitätsgründen beibehalten.

    Andreas Mennel
    quelle
    1

    In Java mag jeder numerische Ausdruck:

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

    x ergibt immer mindestens ein int oder ein long, wenn eines der Additionselemente ein long war.

    Aber es gibt einige harte Macken

    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
    quelle
    1

    AFAIS, niemand erwähnt die finalVerwendung dafür. Wenn Sie Ihr letztes Beispiel ändern und die Variablen a und b als final Variablen definieren, kann der Compiler sicher sein, dass ihre Summe, Wert 5 byte, ohne Genauigkeitsverlust einer Variablen vom Typ zugewiesen werden kann . In diesem Fall kann der Compiler c die Summe von a und b zuweisen. Hier ist der geänderte Code:

    final byte a = 2;
    final byte b = 3;
    byte c = a + b;
    
    snr
    quelle
    Nicht genau. final byte a = (byte) (new Random().nextInt(4));und Incompatible typeserhebt sich wieder hässlicher Kopf. Es ist nicht nur Endgültigkeit, es ist in der Lage, es auf einen Wert zu kompilieren, der dem Typ entspricht.
    LAFK sagt Reinstate Monica
    Sagen Sie Herbert Schildt Ihre Beschwerde, es ist seine Idee. @LIttleAncientForestKami
    snr
    Ich würde, wenn ich ein @snr hätte, zumal nicht einmal Shildt gegen Javac helfen könnte. ;-) Deine Worte implizieren, final dass der Compiler versichert ist , und es kann nicht genug sein. Wenn wir Ihr Beispiel wörtlich nehmen, ist alles in Ordnung und gut. Aber versuchen Sie es b = 3durch b = (byte)(new Random().nextInt(4)). und inkompatible Typen sind zurück und a + b muss erneut gewirkt werden. Vielleicht möchten Sie das zu Ihrer Antwort hinzufügen.
    LAFK sagt Reinstate Monica
    1

    Jeder Datentyp, der niedriger als "int" ist (außer Boolean), wird implizit in "int" konvertiert.

    In deinem Fall:

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

    Das Ergebnis von (a + b) wird implizit in ein int konvertiert. Und jetzt weisen Sie es "kurz" zu. Damit Sie den Fehler erhalten.

    short, byte, char - für all dies erhalten wir den gleichen Fehler.

    Rushikesh Garadade
    quelle
    0

    Ich möchte etwas hinzufügen, auf das nicht hingewiesen wurde. Java berücksichtigt nicht die Werte, die Sie den Variablen (2 und 3) in ...

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

    Soweit Java weiß, könnten Sie dies tun ...

    kurz a = 32767; kurz b = 32767; kurz c = a + b;

    Was außerhalb des Bereichs von short liegen würde, wird das Ergebnis automatisch in ein int gepumpt, da es "möglich" ist, dass das Ergebnis mehr als ein Short, aber nicht mehr als ein Int ist. Int wurde als "Standard" gewählt, da die meisten Leute im Grunde keine harten Codierungswerte über 2.147.483.647 oder unter -2.147.483.648 haben


    quelle
    Unsinn, weil ich zwei int MAXVALUE hinzufügen kann und es nicht erforderlich ist, dass LHS lang ist.
    Gordon