4

I have this statement in my university classes' notes on sub-typing relations:

T is a subtype of S if code written for variables of type S can also be safely used on variables of type T

Section 4.10.1 of the Java Language Spec, describes the sub-typing relationship amongst primitive types in Java. In particular, I use the fact that int <: long i.e int is a sub-type of long as per the JLS.

So if int is a subtype of long, then code written for variables of type long can also be safely used on variables of type int.

This doesn't make sense to me, if I have a piece of code like

long x = 2e63 - 1

Then by the statement above, I can use

int x = 2e63 - 1

which would obviously lead to overflow.

It seems to me that the second part of the statement should be reversed, i.e "T is a subtype of S if code written for a variable of type T can be safely used on variables of type S". However, in my search for an answer it seems that this statement is repeated in other classes' notes elsewhere. Am I simply misunderstanding it? Maybe the example I gave is not a valid case of the statement?

Zabuzard
  • 20,717
  • 7
  • 45
  • 67
TF.Ryan
  • 43
  • 1
  • 3
  • 6
    To be honest, your uni notes don't make a lot of sense. You should ask your professor to clarify what they meant by "code written for variables of type S can also be safely used on variables of type T", and ask this question to _them_, rather than on Stack Overflow. The JLS is the most authoritative document about the Java language, so you did the right thing here - consult the JLS. – Sweeper Jan 14 '21 at 08:44
  • 1
    The uni notes are not precise and only refer to the standard use case of regular classes that extend each other (and also interfaces). So for example if someone wants a `List` you can also give him an `ArrayList` or if someone demands an `Animal` you can give a `Dog` as well. – Zabuzard Jan 14 '21 at 08:50
  • Read 4.10.1 of the JLS again closely. They do not state **subtype** relationships but **supertype** relationships between primitives in that section. So `long > int` does hold in this case for your code sample if you think about it with the correct relation. – T A Jan 14 '21 at 08:54
  • 2
    @Zabuzard Even for reference types that statement isn't true in general... `Dog` is a subtype of `Animal`, but that doesn't mean I can replace the word `Animal` in `Animal a = new Cat();` with `Dog`. – Sweeper Jan 14 '21 at 08:54
  • Also, do not forget about wrapper types. According to the uni notes `int` would be considered subtype of `Integer` since you can do `int value1 = 5; Integer value2 = value1;`. But the JLS does not consider this subtyping at all, just autoboxing. – Zabuzard Jan 14 '21 at 08:55
  • @TA They do however say that subtypes are defined by the supertypes. So if `long > int` then `int < long`. (4.10) – Zabuzard Jan 14 '21 at 08:57
  • 2
    @TA Incorrect, `int <: does="" hold.="" in="" it="" long="" s="" specifies="" that="">1 T implies T <1 S for any types T and S. – Sweeper Jan 14 '21 at 08:58
  • Thanks guys, I'll clarify with my professor – TF.Ryan Jan 14 '21 at 09:19

2 Answers2

2

So if int is a subtype of long, then code written for variables of type long can also be safely used on variables of type int.

OK ... I can see how that is a valid characterization of subtyping.

However, this is not how the JLS actually specifies and uses the subtype relationship <:. In the JLS, subtyping is primarily about the way that values are used.

So, for example, int <: long (int is a subtype of long) means that an int value can be used in a context that requires a long value.

Here are some examples:

 int i = 1;
 long l = i;            // OK - int used where a long is needed
 long ll = i + 1L;      // OK - int used where a long is needed

 public void methodWithLongArg(long arg) { ... }

 methodWithLongArg(i);  // OK - int used where a long is needed

Note that in each of the above, the JLS says that the a primitive widening conversion is used to convert the int value to a long value.

So what about your examples:

long x = 2e63 - 1;   // NOT OK
int i = 2e63 - 1;    // NOT OK

In fact, neither of those are legal Java. The literal 2e63 is a actually a (double) floating point literal. (All literals that use e or E notation are floating point, whether or not there is an explicit decimal point in the literal. And floating point literals without a f or F suffix denote double values.) So 2e63 - 1 evaluates to a double and a double cannot be assigned to a long (or int) unless you explicitly cast it.

That is consistent with the subtyping. The JLS rules mean that long <: double. So that means that a long can be used in a context that requires a double. But in the above, we need a double to be used in a context that requires a long (or int). And that is the opposite of what the subtyping relationships allow.

In fact, there is another problem with your example. 263 is written (in decimal) as 9223372036854775808L. And that can only be used if it is preceded by a unary minus operator. Likewise, it is not expressible as a long literal in Java binary, octal or hexadecimal literal syntaxes.


I think that the real problem is that you are misconstruing what your lecturer has said.

What he is actually saying is that code that has been written for the supertype can be applied to a value of the subtype.

So, in my example, the methodWithLongArg(long arg) method is code that been written for the supertype; i.e. long. It can be used (called) on a value of the subtype; i.e. the value of an int variable.

Am I simply misunderstanding it? Maybe the example I gave is not a valid case of the statement?

Basically, yes. IMO.

Stephen C
  • 632,615
  • 86
  • 730
  • 1,096
  • Oops, the `2e63 - 1` was just meant to represent a value outside the range of an `int` but within range of `long`, didn't realise that it's interpreted as a `double`. – TF.Ryan Jan 14 '21 at 16:58
1

I don't think that int is a real sub-type of long or double. They're just the same integer data-type, but each with a different keyword and a different length. Declaring int <: long and long <: double is required, so that these data-types are known to be lossless cast-able into one direction.

Java byte-code shows how such primitive data-types are handled behind the scenes. Generally long and double are nothing but language constructs, because int is the data-type being used:

Note that any referenced "value" refers to a 32-bit int as per the Java instruction set.

For example: Casting from int to long is no problem at all, but when casting from long to int, the binary representation may exceed the digits available to represent the value properly; if the given value is too large, one would need two int in order to represent one long variable (adding them together by their binary complement won't yield the same value, as it would have to be split into two sets of bits). Look at their binary representations ...those should make the topic obvious.

Martin Zeitler
  • 49,224
  • 12
  • 97
  • 156