Incomparable types

T

Thomas Hawtin

Jono said:
Hi everyone,
I have a reasonably strong .NET background and I have a problem
understanding the Class class in Java. The Type class in .NET (along
with the typeof() operator and System.Object's GetType() method) seems
pretty straightforward to me: an instance of the Type class is
instantiated for every type loaded into the system (even with
generics). But it's not so easy to understand with Java, it seems. If I
try to compile the sample code below it fails on line 3 of the main
method. If I explicitly cast the left-hand and right-hand sides of the
equality expression (as per line 4) to Class, then the compiler is
happy. Please could someone let me know what the subtle difference is
that I am missing out on?
public class NewClass {

public static void main(String[] args)
{
Integer i = new Integer(123);
String s = "456";
//System.out.println(i.getClass() == s.getClass()); //
compilation failure
System.out.println(((Class)i.getClass()) ==
((Class)s.getClass()));
}

}

An object of type Class<capture of ? extends Integer> cannot be the same
object as an object of type Class<capture of ? extends String> (unless
you write unchecked code, and null is an odd case). The is no type T
such that Class<T> is compatible with both of those types. No T
simultaneously extends both String and Integer.

Similarly the expression "i instanceof String" and "i == s" are nonsense
and will not compile.

Tom Hawtin
 
J

Jono

Hi everyone,
I have a reasonably strong .NET background and I have a problem
understanding the Class class in Java. The Type class in .NET (along
with the typeof() operator and System.Object's GetType() method) seems
pretty straightforward to me: an instance of the Type class is
instantiated for every type loaded into the system (even with
generics). But it's not so easy to understand with Java, it seems. If I
try to compile the sample code below it fails on line 3 of the main
method. If I explicitly cast the left-hand and right-hand sides of the
equality expression (as per line 4) to Class, then the compiler is
happy. Please could someone let me know what the subtle difference is
that I am missing out on?
Thanks,
Jono

public class NewClass {

public static void main(String[] args)
{
Integer i = new Integer(123);
String s = "456";
//System.out.println(i.getClass() == s.getClass()); //
compilation failure
System.out.println(((Class)i.getClass()) ==
((Class)s.getClass()));
}

}
 
E

Eric Sosman

Jono wrote On 08/02/06 11:14,:
Hi everyone,
I have a reasonably strong .NET background and I have a problem
understanding the Class class in Java. The Type class in .NET (along
with the typeof() operator and System.Object's GetType() method) seems
pretty straightforward to me: an instance of the Type class is
instantiated for every type loaded into the system (even with
generics). But it's not so easy to understand with Java, it seems. If I
try to compile the sample code below it fails on line 3 of the main
method. If I explicitly cast the left-hand and right-hand sides of the
equality expression (as per line 4) to Class, then the compiler is
happy. Please could someone let me know what the subtle difference is
that I am missing out on?
Thanks,
Jono

public class NewClass {

public static void main(String[] args)
{
Integer i = new Integer(123);
String s = "456";
//System.out.println(i.getClass() == s.getClass()); //
compilation failure
System.out.println(((Class)i.getClass()) ==
((Class)s.getClass()));
}

}

I see nothing wrong with your code -- and neither does
javac. When I remove the //, your code compiles just fine
for me and outputs "false" twice.
 
J

Jono

My compiler version is 1.5.0_07, but I also tried with 1.5.0_06 and got
the same result. The class won't compile if I uncomment the 3rd line of
main().

NewClass.java:23: incomparable types: java.lang.Class<c
apture of ? extends java.lang.Integer> and java.lang.Class<capture of ?
extends
java.lang.String>
System.out.println(i.getClass() == s.getClass());
^
1 error

Eric said:
Jono wrote On 08/02/06 11:14,:
Hi everyone,
I have a reasonably strong .NET background and I have a problem
understanding the Class class in Java. The Type class in .NET (along
with the typeof() operator and System.Object's GetType() method) seems
pretty straightforward to me: an instance of the Type class is
instantiated for every type loaded into the system (even with
generics). But it's not so easy to understand with Java, it seems. If I
try to compile the sample code below it fails on line 3 of the main
method. If I explicitly cast the left-hand and right-hand sides of the
equality expression (as per line 4) to Class, then the compiler is
happy. Please could someone let me know what the subtle difference is
that I am missing out on?
Thanks,
Jono

public class NewClass {

public static void main(String[] args)
{
Integer i = new Integer(123);
String s = "456";
//System.out.println(i.getClass() == s.getClass()); //
compilation failure
System.out.println(((Class)i.getClass()) ==
((Class)s.getClass()));
}

}

I see nothing wrong with your code -- and neither does
javac. When I remove the //, your code compiles just fine
for me and outputs "false" twice.
 
J

Jono

Not having a 1.4.2 compiler at my disposal, I used the online Java
compiler at http://www.innovation.ch/ and the class compiled without
errors. Also, the 1.5.* compiler's error message has something to do
with angle-brackets so I presume that what used to hold true for Java
has been broken by the addition of generics to the language. I'd still
like someone to shed some light on the change, if possible.
Regards,
Jono
My compiler version is 1.5.0_07, but I also tried with 1.5.0_06 and got
the same result. The class won't compile if I uncomment the 3rd line of
main().

NewClass.java:23: incomparable types: java.lang.Class<c
apture of ? extends java.lang.Integer> and java.lang.Class<capture of ?
extends
java.lang.String>
System.out.println(i.getClass() == s.getClass());
^
1 error

Eric said:
Jono wrote On 08/02/06 11:14,:
Hi everyone,
I have a reasonably strong .NET background and I have a problem
understanding the Class class in Java. The Type class in .NET (along
with the typeof() operator and System.Object's GetType() method) seems
pretty straightforward to me: an instance of the Type class is
instantiated for every type loaded into the system (even with
generics). But it's not so easy to understand with Java, it seems. If I
try to compile the sample code below it fails on line 3 of the main
method. If I explicitly cast the left-hand and right-hand sides of the
equality expression (as per line 4) to Class, then the compiler is
happy. Please could someone let me know what the subtle difference is
that I am missing out on?
Thanks,
Jono

public class NewClass {

public static void main(String[] args)
{
Integer i = new Integer(123);
String s = "456";
//System.out.println(i.getClass() == s.getClass()); //
compilation failure
System.out.println(((Class)i.getClass()) ==
((Class)s.getClass()));
}

}

I see nothing wrong with your code -- and neither does
javac. When I remove the //, your code compiles just fine
for me and outputs "false" twice.
 
O

Oliver Wong

Jono said:
My compiler version is 1.5.0_07, but I also tried with 1.5.0_06 and got
the same result. The class won't compile if I uncomment the 3rd line of
main().

NewClass.java:23: incomparable types: java.lang.Class<c
apture of ? extends java.lang.Integer> and java.lang.Class<capture of ?
extends
java.lang.String>
System.out.println(i.getClass() == s.getClass());
^
1 error

See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6448774

- Oliver
 
J

Jono

Hi Oliver,
I believe that the bug you pointed out is specific to one of the beta
versions of Java 6, and the referenced page doesn't give any more clues
as to what's happening when the sample code gets compiled by a Java 5
compiler. I've been reading reams of documents with phrases like type
erasure, reification and synthetic bridge methods and I really hope the
only answer doesn't lie buried inside one of them!
Cheers,
Jono
 
J

Jono

Tom,
Thank you for your explanation, I'm sure it will finally hit home after
a couple of re-reads, but from what I understand the inclusion of
generics has been bundled with a new Class class that has some kind of
[invisible?] object hierarchy of its own. Presumably this hierarchy
exists at some stage during compilation, which is how the compiler
knows that i's type isn't convertible to s's type. If you reflect over
either of the types at runtime they're both the same java.lang.Class,
but then that's probably something to do with the dreaded "type
erasure". I welcome your comments.
Cheers,
Jono

Thomas said:
Jono said:
Hi everyone,
I have a reasonably strong .NET background and I have a problem
understanding the Class class in Java. The Type class in .NET (along
with the typeof() operator and System.Object's GetType() method) seems
pretty straightforward to me: an instance of the Type class is
instantiated for every type loaded into the system (even with
generics). But it's not so easy to understand with Java, it seems. If I
try to compile the sample code below it fails on line 3 of the main
method. If I explicitly cast the left-hand and right-hand sides of the
equality expression (as per line 4) to Class, then the compiler is
happy. Please could someone let me know what the subtle difference is
that I am missing out on?
public class NewClass {

public static void main(String[] args)
{
Integer i = new Integer(123);
String s = "456";
//System.out.println(i.getClass() == s.getClass()); //
compilation failure
System.out.println(((Class)i.getClass()) ==
((Class)s.getClass()));
}

}

An object of type Class<capture of ? extends Integer> cannot be the same
object as an object of type Class<capture of ? extends String> (unless
you write unchecked code, and null is an odd case). The is no type T
such that Class<T> is compatible with both of those types. No T
simultaneously extends both String and Integer.

Similarly the expression "i instanceof String" and "i == s" are nonsense
and will not compile.

Tom Hawtin
 
O

Oliver Wong

Jono said:
Hi Oliver,
I believe that the bug you pointed out is specific to one of the beta
versions of Java 6, and the referenced page doesn't give any more clues
as to what's happening when the sample code gets compiled by a Java 5
compiler. I've been reading reams of documents with phrases like type
erasure, reification and synthetic bridge methods and I really hope the
only answer doesn't lie buried inside one of them!
Cheers,
Jono

Sorry, I didn't realize that Java didn't allow you to compare for
equality two types which "could not possibly be equal", so actually this bug
that I linked to was probably a red herring.

- Oliver
 
P

Patricia Shanahan

Jono said:
Tom,
Thank you for your explanation, I'm sure it will finally hit home after
a couple of re-reads, but from what I understand the inclusion of
generics has been bundled with a new Class class that has some kind of
[invisible?] object hierarchy of its own. Presumably this hierarchy
exists at some stage during compilation, which is how the compiler
knows that i's type isn't convertible to s's type. If you reflect over
either of the types at runtime they're both the same java.lang.Class,
but then that's probably something to do with the dreaded "type
erasure". I welcome your comments.
Cheers,
Jono

Thomas said:
Jono said:
Hi everyone,
I have a reasonably strong .NET background and I have a problem
understanding the Class class in Java. The Type class in .NET (along
with the typeof() operator and System.Object's GetType() method) seems
pretty straightforward to me: an instance of the Type class is
instantiated for every type loaded into the system (even with
generics). But it's not so easy to understand with Java, it seems. If I
try to compile the sample code below it fails on line 3 of the main
method. If I explicitly cast the left-hand and right-hand sides of the
equality expression (as per line 4) to Class, then the compiler is
happy. Please could someone let me know what the subtle difference is
that I am missing out on?
public class NewClass {

public static void main(String[] args)
{
Integer i = new Integer(123);
String s = "456";
//System.out.println(i.getClass() == s.getClass()); //
compilation failure
System.out.println(((Class)i.getClass()) ==
((Class)s.getClass()));
}

}
An object of type Class<capture of ? extends Integer> cannot be the same
object as an object of type Class<capture of ? extends String> (unless
you write unchecked code, and null is an odd case). The is no type T
such that Class<T> is compatible with both of those types. No T
simultaneously extends both String and Integer.

Similarly the expression "i instanceof String" and "i == s" are nonsense
and will not compile.

Tom Hawtin

I can see the rule that is being applied, and it is stated in the JLS,
but the rule itself makes no sense to me.

The language allows many comparisons that can never have a true result.
For example, 2==3 is a valid comparison, with the result false.

Trivially true type comparisons, such as ("A" instanceof String) are
accepted.

It is only in the context of type comparisons, and only if the result is
false, that the compiler rejects the comparison at compile time.

WHY?

Patricia
 
O

Oliver Wong

Patricia Shanahan said:
I can see the rule that is being applied, and it is stated in the JLS,
but the rule itself makes no sense to me.

The language allows many comparisons that can never have a true result.
For example, 2==3 is a valid comparison, with the result false.

Trivially true type comparisons, such as ("A" instanceof String) are
accepted.

It is only in the context of type comparisons, and only if the result is
false, that the compiler rejects the comparison at compile time.

WHY?

It really should be a warning instead of an error: "Warning: this will
always yield true/false, so this is probably not you meant, but the code is
still runnable."

I feel the same way about unreachable code. Sometimes I want to
intentionally make code unreachable for debugging purposes, so I wish
unreachable code was a warning rather than an error. With the Eclipse
compiler, you can actually toggle things like these.

- Oliver
 
C

Chris Uppal

Patricia said:
I can see the rule that is being applied, and it is stated in the JLS,
but the rule itself makes no sense to me.

It makes exactly zero sense to me, too.

If you can convert illegal (illegal!) code into legal code by throwing away
information in the type declarations, then something is badly wrong.

And I most certainly don't agree with Thomas that
"i instanceof String" and "i == s" are nonsense

Both make perfect sense. If they were nonsense then changing the declared type
of "i" and "s" to Object could not possibly make the code coherent -- yet it
does...

Just another cretinous design decision from The Powers That Be (but one made
some time ago, it hasn't been introduced with generics -- which makes a
change).

-- chris
 
J

Jono

Initially after reading Tom's post I disagreed with his idea that a) 'i
== s' and b) 'i instanceof String' were nonsense, but now that it's had
a little while to sink in I am on his side. From a theoretical
perspective, the expressions are nonsense. If you tried to perform the
same operations in C# (a) using an Int32 and a String, and b) using the
'is' operator), you'd get more informative error messages such as:

a) "Operator '==' cannot be applied to operands of type 'int' and
'string'", and
b) "The given expression is never of the provided ('string') type".

This all boils down to the compiler performing type safety checks and
realising that Integer and String aren't in the same branch of the type
hierarchy.

Also, I now realise that the Tiger version of Java's class
java.lang.Class is a generic class whose type parameter denotes the
type that the Class object represents. Because the hierarchy of generic
types is a little complicated (the fictional GenericType<B> does not
derive from GenericType<A>, even though type B extends type A) there is
now no way to compare instances of Class objects because they refer to
different types. Actually, the last statement is a bit of a lie. I've
seen the <?> and <capture of ? extends type> syntax but I haven't
understood it fully yet.

As an aside, I'm enjoying the concept of online compilers such as this
one for C# 2.0: http://www.caller.me.uk/Compilr/C-Sharp.aspx

Cheers,

Jono
 
M

Mike Schilling

Patricia Shanahan said:
I can see the rule that is being applied, and it is stated in the JLS,
but the rule itself makes no sense to me.

The language allows many comparisons that can never have a true result.
For example, 2==3 is a valid comparison, with the result false.

Likewise:

byte b;
...
if (b > 500)

and

Object o;
...
if (o == new Object())

Both are legal yet can never be true.

Trivially true type comparisons, such as ("A" instanceof String) are
accepted.

It is only in the context of type comparisons, and only if the result is
false, that the compiler rejects the comparison at compile time.

It seems as if this is an attempt to make Java seem more strongly typed than
it actually is, so that e.g.

int i;
Integer j;
String s;
...
if (s == j)

is deemed not false but undefined, the way that

if (s == i)

is, in fact, not false but undefined, since equality of references and
scalars is not defined.

This is nonsense, of course. Determining whether two references are equal
is perfectly well defined, as shown by the legality of:

if ((Object)s == (Object)i)

where the casts do not change the value of the references, but simply remove
the compiler's unwillingness to generate the proper code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,265
Messages
2,571,069
Members
48,771
Latest member
ElysaD

Latest Threads

Top