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?
So if
int
is a subtype oflong
, then code written for variables of typelong
can also be safely used on variables of typeint
.
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.