Static fields in specialized classes

Brian Goetz brian.goetz at oracle.com
Wed Oct 15 14:19:37 UTC 2014


Let's take several steps back away from the "I want to continue 
programming with the reflective tools I was comfortable with in Java 
1.0", and see if we can get at the heart of this "augh, different is 
scary!" reaction here that seems to be spreading.

Forget for a second about whether the letters "List<int>.class" have 
semantic meaning in a .java file.  We can certainly hide that, though I 
think (as I said yesterday) you'd probably ultimately complain if we did.

The fundamental question for an implementation of generics is the 
mapping between types and classes.  (List<String> is a type; List.class 
is a class.)

In Java 1.0, there was a clean 1:1 mapping from types to classes.

In Java 5, all instantiations of List<X>, for reference X, mapped to 
class List.class; this is an all-to-1 mapping.  This is simple but 
everyone seems to think this is smelly and complains about it a lot. 
(Too bad, this aspect isn't going away.)

In C++ templates, there is a clean 1:1 mapping again; every 
instantiation of list<x> is a distinct class, with no relationship to 
each other in the type system.  But that distinctness means we give 
something up; for example, there is no "instanceof list" (or cast to 
list) because list is an infinite family of unrelated types.

C# had the benefit of hindsight and designed their VM explicitly to 
support parametric polymorphism, including over value types.  We don't 
have that benefit, and we're not going to redesign the VM for generics 
(sorry).

So the question is, can we implement List<int> with the same class as we 
implement List<String>.  Nearly all the other discomforts follow from 
this same issue (that's what instanceof means, for example).  Can you do 
better?  (I propose you think about this for a good long time before 
responding.)

If you conclude that List<int> is represented by a different class than 
List<String>, now we get to ask some questions.  Should I be able to 
reflect over the members of List<int>?  Should I be able to ask whether 
something is an instance of List<int>?  If so, we have a choice of how 
to spell this, but ultimately that's just a question of syntax, how we 
expose the reality into the surface language.

Whether we add more to reflection -- and we certainly will have to -- is 
another matter, but I think your actual disagreement is with the 
representation of generic types by classes.

(Anyone else find it amusing that people complain when generics are 
erased (all instantiations are represented by a single class), and then 
complain when they are not erased (different instantiations mapped to 
different classes)?)

On 10/15/2014 9:48 AM, Paul Benedict wrote:
> I throw my lot in with Ben and Stephen. I don't think specialization
> should be exposed to the point that you can tell the difference with
> .class. That's very disruptive to code and surprising. Rather, I think
> you should expose more methods on Class to query the specialization type
> -- that was something I recommended in the beginning and still favor.
>
>
> Cheers,
> Paul
>
> On Wed, Oct 15, 2014 at 3:30 AM, Ben Lewis <benlewisj at gmail.com
> <mailto:benlewisj at gmail.com>> wrote:
>
>       I think having Foo.class and Foo<int>.class be different is a mistake.
>
>     class Person{}
>
>     class Employee<any T> extends Bar{
>           //lots of other stuff irrelevant to T
>           T getId(){ … }
>     }
>
>     Person p1=new Employee<Object>();
>     Person p2=new Employee<int>();
>
>     As I understand it
>
>     p1.class==Employee.class;//true
>     p2.class==Employee.class;//false
>     p1 instanceof Employee;//true
>     p2 instanceof Employee;//false
>
>     How to determine b2 comes from ‘source class’ Employee?
>
>     This will cause bugs in situations where the programmer expects how the
>     class hierarchy acts now rather than changing it to where specialisation
>     having a different class. This would also mean migrating <T> to <any T>
>     will cause backward compatibility issues.
>
>     For example
>
>     if(p instanceof Employee){
>         //do something
>     }else{
>         //do something else
>     }
>
>     if written before specialisations It would do as expected but after
>     and if
>     p=new Employee<int>() it would execute the else block not as
>     expected when
>     the code is written.
>
>     On Wed, Oct 15, 2014 at 12:59 PM, Brian Goetz
>     <brian.goetz at oracle.com <mailto:brian.goetz at oracle.com>>
>     wrote:
>
>      > My gut feeling is that this is undesirable and should be disallowed,
>      >> and that perhaps we shouldn't even be able to observe Foo<int>.class
>      >> at all.
>      >>
>      >
>      > If you think about the problem a little more, I think you'll find
>     that
>      > this is wishful thinking.  (But of course, you're free to have a gut
>      > feeling without having thought about it at all.)
>      >
>      >  After all, if specialization is intended to be viewed primarily
>     as an
>      >> implementation detail
>      >>
>      >
>      > It is an implementation detail in the same sense that erasure is;
>     that
>      > generics are implemented by erasure almost certainly leaks into
>     the user's
>      > perception too.  (Erasure is why you can't have
>     Foo<String>.class, for
>      > example.  Among many other things that the user has to pay
>     attention to.)
>      >
>      > class Foo<any T> {
>      >     T get() { ... }
>      > }
>      >
>      > Consider the members of various instantiations of Foo.
>      >
>      > The signature of get() in Foo<String> is "String get()", but the
>     signature
>      > of get() in Foo<int> is "int get()".  The erasure of the first is
>     "Object
>      > get()"; the erasure of the second is "int get()".  While all erased
>      > reference instantiations can be implemented by the same class, the
>      > primitive/value instantiations cannot be.
>      >
>      > Similarly, Foo<int> can't extend Foo<?> (because that really
>     means Foo<?
>      > extends Object>, and has to for compatibility reasons), nor can
>     it extend
>      > raw Foo.  (Which is fine; raw types exist solely to support
>     migration from
>      > ungenerified codebases to generified ones.)
>      >
>      > We could hide the existence of List<int>.class, but that would
>     just make
>      > life harder for users, who would then have to jump through bigger
>     hoops to
>      > reflect over the members of List<int> (which are different from
>     the members
>      > of List<String>, even after erasure.)
>      >
>      >  In other words, while the Java language/library changes may be
>      >> expressed by some improvements to reflection, I'd be concerned about
>      >> the many implemented algorithms based on reflection.
>      >>
>      >
>      > It is of course reasonable to be concerned.  Stay tuned and see
>     if your
>      > concerns are addressed.
>      >
>
>


More information about the valhalla-dev mailing list