14

I noticed, one interesting thing.
Java's Integer.MAX_VALUE is 0x7fffffff (2147483647)
Kotlin's Int.MAX_VALUE is 2147483647
but if you write
in Java:
int value = 0xFFFFFFFF; //everything is fine (but printed value is '-1')
in Kotlin:
val value: Int = 0xFFFFFFFF //You get exception The integer literal does not conform to the expected type Int

Interesting right? So you're able to do something like new java.awt.Color(0xFFFFFFFF, true) in Java but not in Kotlin.

Color class works with that int on "binary" level, so everything works fine for both platforms with all constructors (Color(int rgba) or Color(int r, int g, int b, int a)).
Only workaround which I found for kotlin is java.awt.Color(0xFFFFFFFF.toInt(), true).

Any idea why is it like this in Kotlin?

Kikju
  • 787
  • 1
  • 6
  • 17
  • 1
    I think Java automatically widens the number, `Int` to `Long`. --- See https://kotlinlang.org/docs/reference/basic-types.html --- and also see http://www.java2s.com/Tutorial/SCJP/0080__Type-Casting/WideningandNarrowconversions.htm --- In Kotlin you have to do it explicitly, no language automatism to save you from having chosen a too narrow type. – Mörre Nov 23 '17 at 20:29
  • 3
    @Mörre `0xFFFFFFFF` is a valid two's complement representation of `-1` as an `int`. The difference between the two languages is that Kotlin literals don't seem to accept two's complement notation; the literal `0x80000000` is an error (in Java, it's `Integer.MIN_VALUE`). In other words, you must use a unary `-` operator to represent negative literals. Although I can't find any specific mention of this in the spec of either language. – bcsb1001 Nov 23 '17 at 20:37
  • @bcsb1001 Please look at `Integer.MAX_VALUE` – Mörre Nov 23 '17 at 20:38
  • What about it? I don't see anything about it that contradicts my comment. – bcsb1001 Nov 23 '17 at 20:39
  • @bcsb1001 I don't see how yours contradicts mine. See comments below, Paul's answer. – Mörre Nov 23 '17 at 20:39
  • 1
    @Mörre Because there is no widening conversion happening, which you seem to suggest there is. – bcsb1001 Nov 23 '17 at 20:40
  • 1
    @bcsb1001 See below, comments, Paul's answer. – Mörre Nov 23 '17 at 20:41
  • 4
    @Mörre There's no widening in applying an int value to an int field. – shmosel Nov 23 '17 at 20:42
  • 2
    See https://discuss.kotlinlang.org/t/when-does-bit-fiddling-come-to-kotlin/2249 - *The current master plan is (...) have large hex constants have unsigned types (i.e. 0xFFFFFFFF is not a negative Int, but an unsigned Int, i.e. UInt)*. It's coming from wanting to protect you from unexpected overflow nuances, but 99.9% of the time you use hex you probably care about the bits, not the actual value. – Mark Peters Nov 23 '17 at 20:56

3 Answers3

6

This is partially answered here:

In Kotlin you need to prepend the - sign to denote negative Int which is not true in Java.

So it seems that Java will interpret hex literals as signed, whereas Kotlin will treat them as unsigned.

The negation would have to be done manually.

Small aside: JetBrains' Kotlin converter actually converts

int a = 0xffffffff;

to

var a = -0x1

but this may just be it realizing exactly what you have noticed.


The part of the spec for hexadecimal literals doesn't mention this at all, however.

Salem
  • 13,516
  • 4
  • 51
  • 70
3

I think, this problem should be solved by Kotlin 1.3 and UInt See more here: https://kotlinlang.org/docs/reference/whatsnew13.html#unsigned-integers

Kikju
  • 787
  • 1
  • 6
  • 17
0

The explanation is in the reference docs:

Due to different representations, smaller types are not subtypes of bigger ones. If they were, we would have troubles of the following sort:

// Hypothetical code, does not actually compile:
val a: Int? = 1 // A boxed Int (java.lang.Integer)
val b: Long? = a // implicit conversion yields a boxed Long (java.lang.Long)
print(a == b) // Surprise! This prints "false" as Long's equals()
              // check for other part to be Long as well

So not only identity, but even equality would have been lost silently all over the place.

As a consequence, smaller types are NOT implicitly converted to bigger types. This means that we cannot assign a value of type Byte to an Int variable without an explicit conversion.

Paul Hicks
  • 13,289
  • 5
  • 51
  • 78
  • 2
    This isn't the question. In Java, `0xFFFFFFFF` would still be an `int`. Additionally `int` is definitely not larger than `long` – Salem Nov 23 '17 at 20:38
  • 1
    Yes, I am sure. `0xFFFFFFFF` when interpreted as signed is clearly `-1` (and is 32 bits). – Salem Nov 23 '17 at 20:44
  • Does this comment from that linked answer help? "You can write int i = (int)Long.MAX_VALUE; and it will be -1. I agree that it is weird, because _compiler complains on int i = 4294967295_; but doesn't complain on int i = 0xffffffff;. I think, basically this is because 0x... is treated as a bit sequence and then is casted to the desired type." – Paul Hicks Nov 23 '17 at 20:46
  • But fair call, this answer doesn't cover the question. I'll leave it to accrue downvotes :) – Paul Hicks Nov 23 '17 at 20:47
  • 3
    That comment is actually exactly my point. `0xFFFFFFFF` _is_ an `int`. Same as `0b11111111111111111111111111111111`, when treated as a 32 bit integer the high bit is `1` and due to representation of signed integers this has a value of `-1`. When treated as any wider type (i.e. `0xFFFFFFFFL`) the value is of course positive, though ([see this answer](https://stackoverflow.com/a/319206/7366707)) – Salem Nov 23 '17 at 20:48