Thursday, March 3, 2011

Can I store the generics attribute into an object's field

Assuming the following class stub:

public class Foo<T> {
  private Class<T> type;

  public Foo<T> () {}
}

Can I store the generic type T into the field type in the constructor, without changing the constructor's signature to "public Foo<T> (Class<T> type)"?

If yes, how? If no, why?

"type = T" doesn't seem to work.

From stackoverflow
  • Does

    type = T.getClass();
    

    work?

    Henrik Paul : no, it doesn't work, because I can't refer to T inside the method ("T cannot be resolved")
  • You should try this maybe:

    type = T.GetType();
    
    Henrik Paul : no, it doesn't work, because I can't refer to T inside the method ("T cannot be resolved").
  • No - type erasure means that you have to provide something at execution time. You could provide either an instance of T or Class<T> but the class itself will just have every occurrence of T replaced with Object.

    This kind of thing is one of the biggest disadvantages of Java's generics vs those in .NET. (On the other hand, the variance story is stronger - if more confusing - in Java than in .NET.)

  • Well, why not have an instance of T in your class? I mean what does T buy you anything if this is not the case? So if yout have T x, they you cando an x.GetType().

    Charles Graham : Hmm, I thought this was .Net. I forgot that Java as different generics.
  • As said in this thread,

    for Example List<Integer>

    list.get (index).getClass()
    

    will not give you type of object stored in list:

    • when the list is empty
    • even when the list is NOT empty, because any element can be any subclass of the generic type parameter.

    Since type parameters are erased at compile time, they do not exist at runtime (sorry Jon, at execution time): Hence you cannot get generic type info via reflection in java.

    There is a case for more reification which is on the table (Java 7 ?), and would facilitate what you are after.

    One would hope that a bad type, casted into T, would provoke a Cast exception that would, at execution time, reveal the original type used for T, but alas, that does not work either.

    Consider the following test class:

    import java.util.ArrayList;
    
    /**
     * @param <T>
     */
    public class StoreGerenericTypeInField<T>
    {
    
        private T myT = null;
        private ArrayList<T> list = new ArrayList<T>();
    
        private void setT(final T aT) { myT = aT; }
        /**
         * Attempt to do private strange initialization with T, in the hope to provoke a cast exception
         */
        public StoreGerenericTypeInField()
        {
         StringBuilder aFirstType = new StringBuilder();
         StringBuffer aSecondType = new StringBuffer();
         this.list.add((T)aFirstType);
         this.list.add((T)aSecondType);
         System.out.println(this.list.get(0).getClass().getName());
         System.out.println(this.list.get(1).getClass().getName());
    
         setT((T)aFirstType);
         System.out.println(this.myT.getClass().getName());
         setT((T)aSecondType);
         System.out.println(this.myT.getClass().getName());
        }
        /**
         * @param args
         */
        public static void main(String[] args)
        {
         StoreGerenericTypeInField<Integer> s = new StoreGerenericTypeInField<Integer>();
        }
    }
    

    The constructor attempt to store nasty thing into its T variable (or its List of T)... and everything run smoothly!?

    It prints:

    java.lang.StringBuilder
    java.lang.StringBuffer
    java.lang.StringBuilder
    java.lang.StringBuffer
    

    The type erasure in in itself is not to blame... that is the combination of type erasure (new to java) and unsafe casting (java legacy old feature) which makes T truly out of reach at execution time (until you actually try to use it with 'T' specific methods)

    Jon Skeet : Upvoted mostly for the amazement factor that you not only read that bit, but remembered it and bothered to find the right link :) (Oh, and because you're correct of course!)
    VonC : Hmmm.... That would be mostly because I am in the process of watching your Copenhagen videos (I am at DEL 4) http://msmvps.com/blogs/jon_skeet/archive/2008/11/19/copenhagen-c-talk-videos-now-up.aspx. You began that presentation by reminding your audience about that distinction.
    Jon Skeet : You're disturbing me more and more... Shame that I got it wrong almost *every* time I mentioned runtime/execution time during the day...
    VonC : And yes, Jon, I will buy your book, I promise ;)
    Jon Skeet : I disagree with the idea that type erasure isn't a problem. It means you just can't do T.class etc which is *so* handy. It may not be at fault for this particular code, but it's still a pain :(
    Jon Skeet : And note that the compiler *knows* we're using unsafe/unchecked operations here - it's not like it compiles without warning.

0 comments:

Post a Comment