Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > Java generics and type erasure

Reply
Thread Tools

Java generics and type erasure

 
 
Marcin Pietraszek
Guest
Posts: n/a
 
      05-23-2011
Hi!

Some time ago I've encountered stange behaviour while using generics,
small example is provided in gist:

https://gist.github.com/977599

Anybody could explain me why in second example (line commended with
"compilation failure") compilation fails? Do you know any detailed
description on how and when type erasure works in java?

--
greetings
Marcin Pietraszek
 
Reply With Quote
 
 
 
 
John B. Matthews
Guest
Posts: n/a
 
      05-24-2011
In article
<9d4c2b16-beb5-40b1-87a2->,
Marcin Pietraszek <> wrote:

> Some time ago I've encountered stange behaviour while using generics,
> small example is provided in gist:
>
> https://gist.github.com/977599
>
> Anybody could explain me why in second example (line commended with
> "compilation failure") compilation fails?


You neglected to specify the type parameter for foo2, specified in the
declaration Foo<T>. Without the actual type, <Boolean>, the compiler
can only infer that get() returns Object, as would have been the case
prior to generics:

import java.util.*;

public class Foo<T> {

private Map<String, Integer> bar = new HashMap<String, Integer>();

public static void main(String... args) {
Foo<Boolean> foo1 = new Foo<Boolean>();
Integer x1 = foo1.bar.get("x"); // ok

Foo<Boolean> foo2 = new Foo<Boolean>();
Integer x2 = foo2.bar.get("x"); // compilation failure
}

> Do you know any detailed description on how and when type erasure
> works in java?


"All of these parameterized types share the same class at runtime."

<http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.2>

See also, Bloch, ch. 5:

<http://java.sun.com/docs/books/effective/>

--
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>
 
Reply With Quote
 
 
 
 
Lew
Guest
Posts: n/a
 
      05-24-2011
John B. Matthews wrote:
> Marcin Pietraszek<> wrote:
>
>> Some time ago I've encountered stange behaviour while using generics,
>> small example is provided in gist:
>>
>> https://gist.github.com/977599


You should post the code in the message instead of out of band.

import java.util.*;

public class Foo<T> {

private Map<String, Integer> bar = new HashMap<String, Integer>();

public static void main(String ... args) {
Foo<Boolean> foo1 = new Foo<Boolean>();
Integer x1 = foo1.bar.get("x"); // ok

Foo foo2 = new Foo<Boolean>();
Integer x2 = foo2.bar.get("x"); // compilation failure
}

}

>> Anybody could explain me why in second example (line commended with
>> "compilation failure") compilation fails?


> You neglected to specify the type parameter for foo2, specified in the
> declaration Foo<T>. Without the actual type,<Boolean>, the compiler
> can only infer that get() returns Object, as would have been the case
> prior to generics:
>
> import java.util.*;
>
> public class Foo<T> {
>
> private Map<String, Integer> bar = new HashMap<String, Integer>();
>
> public static void main(String... args) {
> Foo<Boolean> foo1 = new Foo<Boolean>();
> Integer x1 = foo1.bar.get("x"); // ok
>
> Foo<Boolean> foo2 = new Foo<Boolean>();
> Integer x2 = foo2.bar.get("x"); // compilation failure
> }
>
>> Do you know any detailed description on how and when type erasure
>> works in java?


> "All of these parameterized types share the same class at runtime."
>
> <http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.2>
>
> See also, Bloch, ch. 5:
>
> <http://java.sun.com/docs/books/effective/>


It's subtler than that. The generic parameter is on 'Foo', not the map. The
map type does not depend on the type parameter. So the question is why the
'get()' returns 'Object'. Naively, one would expect the type of 'bar' to
resolve to 'Map<String, Integer>' no matter what '<T>' is, or isn't.

The only thing I can think of is that leaving the type parameter out in the
containing class means that all bets are off, and the compiler gives up on
generics throughout that variable's depth. Thus it doesn't even bother to
parse the 'bar' parameters, defaulting thus to '<?,?>'. Consequently, the
type of the expression 'foo2.bar.get("x")' is 'Object'. The assignment target
of that expression is 'Integer', and that requires an explicit downcast,
omitted in the code along with the type parameter.

This is an object lesson (pun intended) in how bad it really can be to omit
the type parameter.

I haven't looked up chapter and verse on this reasoning yet. Anyone care to
rise to the challenge?

A couple of observations:

Type erasure has absolutely nothing to do with this. Type erasure happens at
run time. It will never create a compiler error.

The direct dot reference to a 'private' member is a bit dodgy, though allowed
in the class's own 'main()'.

--
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedi.../c/cf/Friz.jpg
 
Reply With Quote
 
Marcin Pietraszek
Guest
Posts: n/a
 
      05-24-2011
> John B. Matthews wrote:
>
> It's subtler than that. *The generic parameter is on 'Foo', not the map.. *The
> map type does not depend on the type parameter. *So the question is whythe
> 'get()' returns 'Object'. *Naively, one would expect the type of 'bar' to
> resolve to 'Map<String, Integer>' no matter what '<T>' is, or isn't.
>
> The only thing I can think of is that leaving the type parameter out in the
> containing class means that all bets are off, and the compiler gives up on
> generics throughout that variable's depth. *Thus it doesn't even botherto
> parse the 'bar' parameters, defaulting thus to '<?,?>'. *Consequently, the
> type of the expression 'foo2.bar.get("x")' is 'Object'. *The assignmenttarget
> of that expression is 'Integer', and that requires an explicit downcast,
> omitted in the code along with the type parameter.
>
> This is an object lesson (pun intended) in how bad it really can be to omit
> the type parameter.
>
> I haven't looked up chapter and verse on this reasoning yet. *Anyone care to
> rise to the challenge?


To be honest before asking on group I've tried to search the answer in
JLS, unfortunatelly without any success -- I have same observations as
yours but I didn't manage to find exact explanation.

--
greegings
Marcin Pietraszek
 
Reply With Quote
 
Ian Shef
Guest
Posts: n/a
 
      05-24-2011
Lew <> wrote in news:irfgkl$lca$:

> John B. Matthews wrote:
>> Marcin Pietraszek<> wrote:

<snip>
>
>>> Anybody could explain me why in second example (line commended with
>>> "compilation failure") compilation fails?

>
>> You neglected to specify the type parameter for foo2, specified in the
>> declaration Foo<T>. Without the actual type,<Boolean>, the compiler
>> can only infer that get() returns Object, as would have been the case
>> prior to generics:
>>
>> import java.util.*;
>>
>> public class Foo<T> {
>>
>> private Map<String, Integer> bar = new HashMap<String,
>> Integer>();
>>
>> public static void main(String... args) {
>> Foo<Boolean> foo1 = new Foo<Boolean>();
>> Integer x1 = foo1.bar.get("x"); // ok
>>
>> Foo<Boolean> foo2 = new Foo<Boolean>();
>> Integer x2 = foo2.bar.get("x"); // compilation failure
>> }
>>
>>> Do you know any detailed description on how and when type erasure
>>> works in java?

>
>> "All of these parameterized types share the same class at runtime."
>>
>> <http://java.sun.com/docs/books/jls/t...asses.html#8.1.
>> 2>
>>
>> See also, Bloch, ch. 5:
>>
>> <http://java.sun.com/docs/books/effective/>

>
> It's subtler than that. The generic parameter is on 'Foo', not the map.
> The map type does not depend on the type parameter. So the question is
> why the 'get()' returns 'Object'. Naively, one would expect the type of
> 'bar' to resolve to 'Map<String, Integer>' no matter what '<T>' is, or
> isn't.
>
> The only thing I can think of is that leaving the type parameter out in
> the containing class means that all bets are off, and the compiler gives
> up on generics throughout that variable's depth.

Correct (although "gives up" is an unfair characterization). One of the
things that I learned recently about generics is that using a raw type
e.g. Foo foo2 = ...
causes the compiler to treat everything within that type as raw (with respect
to the particular variable that was declared with the raw type).
That is, any use of foo will now be treated as if everything within the Foo
class was defined as a raw type. Thus,
private Map<String, Integer> bar = new HashMap<String, Integer>();
now gets treated as if it was written
private Map bar = new HashMap();

Now (as far as the compiler is concerned) foo2.bar.get("x") produces an
Object and not the Integer that one naively might expect.

> Thus it doesn't even
> bother to parse the 'bar' parameters, defaulting thus to '<?,?>'.
> Consequently, the type of the expression 'foo2.bar.get("x")' is
> 'Object'. The assignment target of that expression is 'Integer', and
> that requires an explicit downcast, omitted in the code along with the
> type parameter.
>
> This is an object lesson (pun intended) in how bad it really can be to
> omit the type parameter.
>
> I haven't looked up chapter and verse on this reasoning yet. Anyone
> care to rise to the challenge?

It better be someplace in the JLS but I have not looked. I prefer to use
Angelika Langer's Java Generics FAQs. FAQ_203 says (in part):

"Fields of a raw type have the type that they would have after type erasure."
See
<http://www.angelikalanger.com/Generi...rizedTypes.htm
l#FAQ203>

That is somewhat subtle but describes the situation observed here. I have
seen a clearer explanation elsewhere recently but cannot recall where.
Sorry.

>
> A couple of observations:
>
> Type erasure has absolutely nothing to do with this. Type erasure
> happens at run time. It will never create a compiler error.


Au contraire. The Java Tutorial says "When a generic type is instantiated,
the compiler translates those types by a technique called type erasure — a
process where the compiler removes all information related to type parameters
and type arguments within a class or method."
See
<http://download.oracle.com/javase/tutorial/java/generics/erasure.html>

I interpret this to mean that type erasure happens at compile time - not at
run time.

>
> The direct dot reference to a 'private' member is a bit dodgy, though
> allowed in the class's own 'main()'.
>


 
Reply With Quote
 
Lew
Guest
Posts: n/a
 
      05-24-2011
Ian Shef wrote:
> Lew wrote:
>> Type erasure has absolutely nothing to do with this. Type erasure
>> happens at run time. It will never create a compiler error.


> Au contraire. The Java Tutorial says "When a generic type is instantiated,
> the compiler translates those types by a technique called type erasure ďż˝ a
> process where the compiler removes all information related to type parameters
> and type arguments within a class or method."
> See
> <http://download.oracle.com/javase/tutorial/java/generics/erasure.html>
>
> I interpret this to mean that type erasure happens at compile time - not at
> run time.


OK, I see what you mean, but that's only after the compiler's enforcement of
the type parameters. If they don't fly, the erasure doesn't happen. So what
I meant, and said incorrectly, that *to the compiler*, the generic type
exists, but *to the runtime* it's already been erased. So the fact that the
type parameter has been erased shows up at run time, and the type information
in the parameter shows up at compile time.

You are correct. The difference between how you and I think of it is that I'm
thinking in terms of where you see the type-parameter errors, that is, on
input to the compiler. You're seeing where the actual erasure occurs, that
is, on output from the compiler.

I will be more rigorous in my explanation of that henceforth. Thanks for the
nuance.

--
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedi.../c/cf/Friz.jpg
 
Reply With Quote
 
Ian Shef
Guest
Posts: n/a
 
      05-24-2011
Lew <> wrote in news:irh6kb$gse$:

<snip>
> OK, I see what you mean, but that's only after the compiler's
> enforcement of the type parameters. If they don't fly, the erasure
> doesn't happen. So what I meant, and said incorrectly, that *to the
> compiler*, the generic type exists, but *to the runtime* it's already
> been erased. So the fact that the type parameter has been erased shows
> up at run time, and the type information in the parameter shows up at
> compile time.


Sounds like we are in violent agreement.
>
> You are correct. The difference between how you and I think of it is
> that I'm thinking in terms of where you see the type-parameter errors,
> that is, on input to the compiler. You're seeing where the actual
> erasure occurs, that is, on output from the compiler.


More violent agreement: Type erasure takes place at compile time and
becomes visible at run time.
>
> I will be more rigorous in my explanation of that henceforth. Thanks
> for the nuance.
>


OK. I didn't mean to get pedantic but my fingers ran away on their own.
Be rigorous if you want to; some will get the nuance (I'm glad you did) and
some won't.

By the way, the description of the particular situation examined here
(where instance fields of a type are treated as raw because the type itself
is used raw) seem to be covered by version 3 of the JLS in paragraph 4.8.
I am not sure exactly which sentence in 4.8 covers this, but I am sure that
it is there because the examples show similar situations. Perhaps it is
covered by this:

"The type of a constructor (§8., instance method (§8.8, §9.4), or non-
static field (§8.3) M of a raw type C that is not inherited from its
superclasses or superinterfaces is the erasure of its type in the generic
declaration corresponding to C."



 
Reply With Quote
 
Lawrence D'Oliveiro
Guest
Posts: n/a
 
      05-25-2011
In message <irhhik$9rc$>, Susan Calvin wrote:

> The only reason I can think of for not doing this (the logic seems simple
> enough to implement) is that it turned out doing so would break legacy
> code that used util collections' raw types. Was that why?


Yup. Much of the complexity attendant on introducing generics into Java was
precisely because of that need for backward compatibility.

> Perhaps there should be a compile flag that turns on the legacy-compatible
> behavior for use when compiling 1.4 and older sources, but which is off by
> default?


What happens when you mix code compiled with that flag, with code that was
compiled without?

 
Reply With Quote
 
Susan Calvin
Guest
Posts: n/a
 
      05-26-2011
On Thu, 26 May 2011 10:18:20 +1200, Lawrence D'Oliveiro wrote:

> In message <irhhik$9rc$>, Susan Calvin wrote:
>
>> The only reason I can think of for not doing this (the logic seems
>> simple enough to implement) is that it turned out doing so would break
>> legacy code that used util collections' raw types. Was that why?

>
> Yup. Much of the complexity attendant on introducing generics into Java
> was precisely because of that need for backward compatibility.
>
>> Perhaps there should be a compile flag that turns on the
>> legacy-compatible behavior for use when compiling 1.4 and older
>> sources, but which is off by default?

>
> What happens when you mix code compiled with that flag, with code that
> was compiled without?


Why, nothing, of course, since generics don't exist at run-time. Both

Integer x = aFoo.m1.get("quux");

(in the file compiled with the flag) and

Integer x = (Integer)(aFoo.m1.get("quux"));

(in the file compiled without it) would compile to the same bytecode,
including a checkcast for Integer.

This is only an issue for source compatibility, not binary compatibility.

And there are far worse problems with generics and especially with
autoboxing. For example:

int x = aFoo.m1.get("quux");

Guess what happens if "quux" is not found? There should probably be a
shorthand way to check for this -- maybe a variation on the ?: operator
that tests its left hand side for null, evaluates to it if it's not, and
evaluates to its right hand side otherwise, e.g.

int x = aFoo.m1.get("quux")?:-1

perhaps would make x -1 as a sentinel for "not found" in this instance.

Actually, the Java 5 features have several rough spots that aren't easy
to smooth over. Java really should have been designed with generics and
better integration of primitive types into the type system from the
beginning. Now we have these ad hoc bandaid solutions that show visible
seams here and there and we're probably going to have to live with them
for the next forty years, just as we're living now with 40-year-old COBOL
code.
 
Reply With Quote
 
Lawrence D'Oliveiro
Guest
Posts: n/a
 
      05-26-2011
In message <irk9dr$j5p$>, Susan Calvin wrote:

> On Thu, 26 May 2011 10:18:20 +1200, Lawrence D'Oliveiro wrote:
>
>> In message <irhhik$9rc$>, Susan Calvin wrote:
>>
>>> Perhaps there should be a compile flag that turns on the
>>> legacy-compatible behavior for use when compiling 1.4 and older
>>> sources, but which is off by default?

>>
>> What happens when you mix code compiled with that flag, with code that
>> was compiled without?

>
> Why, nothing, of course, since generics don't exist at run-time.


If theat were the case, then there would be no backward-compatibility issue,
would there? And the whole machinery of “raw” types could just disappear in
a puff of un-necessity.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: Dynamic Casting with Generics: Type Erasure Problems William Java 3 03-04-2011 06:08 PM
Dynamic Casting with Generics: Type Erasure Problems William Java 5 03-03-2011 06:22 PM
Generics and type erasure Martin Lorentzson Java 2 11-06-2006 09:02 PM
Generics: struggling against type erasure... z-man Java 8 10-09-2006 07:53 AM
Java Generics, Type Erasure and Frameworks Sebastian Millies Java 1 10-07-2004 10:10 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57