首页 > 解决方案 > Long Literals in Java

问题描述

So reading through the documentation, it states that if you declare an int literal with an L on the end, it will read it as a long instead. Here is my question: is there any difference between this and just naming the type as a long to start with? For example:

int x = 100L;
VS.
long x = 100;

The same thing?

标签: javalong-integer

解决方案


tl;dr

is there any difference … int x = 100L; versus long x = 100;

Yes. There is a major difference.

  • The first will not compile, and will not run. You cannot cram a 64-bit long into a 32-bit int.
  • The second is the opposite of the first, a widening of a 32-bit int primitive integer literal while being assigned to a 64-bit long primitive integer variable.

Details

There is much misinformation in the other Answers.

32-bit vs 64-bit integer primitives

The L seen above means “<a href="https://en.wikipedia.org/wiki/64-bit_computing" rel="nofollow noreferrer">64-bit int integer primitive” whereas the absence of a L in an integer literal means “<a href="https://en.wikipedia.org/wiki/32-bit" rel="nofollow noreferrer">32-bit int integer primitive”.

The following line fails to compile. You are attempting to place a 64-bit long primitive literal into a 32-bit int variable. Square peg, round hole. One of the compiler’s jobs is to stop such nonsense.

int x = 100L ;  // BAD — compiler fails — Cannot place a 64-bit `long` `int` primitive in a 32-bit `int` variable.

Error … incompatible types: possible lossy conversion from long to int

Let’s correct that line by dropping the L, assigning a 32-bit int literal to a 32-bit int variable.

int x = 100 ;  // 32-bit `int` integer primitive literal being stored in a 32-bit `int` integer variable. No problem, no issues.

Primitives, not objects

Notice that, contrary to some other Answers seen on this page, the code above has no objects, only primitives.

Widening from 32-bit to 64-bit

In the next line, you are first creating a 32-bit int primitive with the “100” part. That 32-bit int primitive is then assigned to a 64-bit long primitive. Java fills in the extra thirty-two bits with zeros, so effectively you end up with the same number.

long x = 100 ;  // 32-bit `int` integer primitive being stored in a 64-bit `long` integer primitive. The extra 32-bits are filled in with zeros automatically by Java.

As noted in a comment by Andreas, this conversion from 32-bit to 64-bit integers is technically called widening. See JLS 5.1.2 Widening Primitive Conversion for a technical discussion.

Some people, including me, consider such code using literals that depend on automatic widening to be poor form. Your intentions as a programmer are ambiguous. So I would write that code using the appended L, like this long x = 100L ;. But some folks would consider this position to be needlessly worried about an inconsequential matter.

Casting

Contrary to some other Answers, there is no casting involved in the code seen above.

Here is an example of casting. We start with a 64-bit long primitive. When then cast to an int primitive, lopping off the higher 32-bits. The (int) tells the compiler “yes, I know I am risking data loss in chopping off 32 of my 64 bits, but go ahead and do it, I am taking responsibility for this act”.

int x = (int) 100L ;  // Start with a 64-bit `long` primitive literal, lop off 32 of the 64 bits, resulting in a 32-bit `int` primitive being assigned to a 32-bit primitive variable.

In this particular example, there is no problem, as a value of one hundred fits with the lower 32-bits, so the set of higher thirty-two bits being excised are all zeros. So no damage in this case. But also in this case, this code is senseless, and should not be done in real work. Indeed, in real work you would only rarely, if ever, have a productive reason to lop off half of a 64-bit integer’s bits by casting.

Narrowing with Math.toIntExact

A better alternative to casting is to call Math.toIntExact. You pass a long primitive to this method, and it returns an int, the result of narrowing the 64-bit integer to 32-bit integer. The advantage over casting is that an ArithmeticException is thrown if overflow occurs. So you will be informed of any data loss.

try {
    int x = java.lang.Math.toIntExact( 100L ) ;
} catch ( ArithmeticException e ) {
    … // Handle data loss, the overflow in going from 64-bits to 32-bits.
}

Objects

Because some other Answers incorrectly raised the subject of objects and auto-boxing, I'll mention a bit about that.

The wording Long with an uppercase L in the front means the Long class rather than the long primitive. I'll not explain the distinction here, except to say I wish Java had never made explicit use of primitives and had instead stuck to only classes. Indeed, there is a remote possibility that a version of Java in the far future may do exactly that (hide the existence of primitives).

But here and now, there is a distinction between classes/objects and primitives. To help smooth over that distinction, Java supports auto-boxing. As a convenience, in most scenarios the Java compiler and runtime can detect when your code is assigning a primitive to where an object is expected, and vice-versa.

Long myLongObject = 100L ;  // This works because Java automatically detects the primitive `long` being assigned to an object of class `Long`, and instantiates a `Long` object to hold that number value.

That line above effectively is treated as if it is:

Long myLongObject = Long.valueOf( 100L ) ;

And is would be logically equivalent to the following, but technically this next line fails (compiler error) because there is no need for the L in the string as the string is assumed to contain digits of a 64-bit integer. In other words, this input string is not an integer literal, so the L is superfluous and not allowed.

Long myLongObject = Long.valueOf( "100L" ) ;  // Compiler error. The `L` is not allowed because it is redundant. 

Just drop the L from that String input, as the input is assumed to represent a 64-bit number.

Long myLongObject = Long.valueOf( "100" ) ;

Java will also automatically widen from a 32-bit int before also doing the autoboxing. So, the lines above are also effectively the same as this.

Long myLongObject = Long.valueOf( 100 ) ;  // 32-bit `int` primitive literal automatically widened to a 64-bit `long`, then passed as argument to the `valueOf` method that takes only a `long`. 

Again, the code seen in the Question has no involvement with classes/objects. This section of this Answer would be irrelevant except that other incorrect Answers raised the issues of objects & auto-boxing.


推荐阅读