6 Replies Latest reply: Jun 28, 2011 11:32 AM by Darryl Burke RSS

    Any way to get the Class from a generic Type?

    Darryl Burke
      I wanted to male a generic class to map static fields of a class, of the same type (think <tt>Color.RED</tt>) and the only way I could find to obtain the Class/Fields was by passing the class literal to the constructor. Is there a way to make this with a no-argument constructor?
      package darrylbu.util;
      
      import java.lang.reflect.*;
      import java.util.*;
      
      public class StaticFieldMap<T> extends HashMap<String, T> {
      
        @SuppressWarnings("unchecked")
        public StaticFieldMap(Class<T> clazz) {
          Field[] fields = clazz.getDeclaredFields();
          for (Field field : fields) {
            if (field.getType() == clazz) {
              try {
                put(field.getName(), (T) field.get(null));
              } catch (IllegalArgumentException ex) {
                ex.printStackTrace();
              } catch (IllegalAccessException ex) {
                ex.printStackTrace();
              }
            }
          }
        }
      }
      Since the usual question for anything involving Reflection is usually "why?" I'm including one possible use case.
      import darrylbu.util.StaticFieldMap;
      import java.awt.Color;
      import java.util.Map;
      import javax.swing.*;
      import javax.swing.event.ListSelectionEvent;
      import javax.swing.event.ListSelectionListener;
      
      public class ColorMapDemo {
      
        public static void main(String[] args) throws Exception {
          SwingUtilities.invokeLater(new Runnable() {
      
            @Override
            public void run() {
              new ColorMapDemo().makeUI();
            }
          });
        }
      
        public void makeUI() {
          final Map<String, Color> colorMap = new StaticFieldMap<Color>(Color.class);
          String[] data = colorMap.keySet().toArray(new String[] {});
          final JPanel panel = new JPanel();
          final JList list = new JList(data);
          list.addListSelectionListener(new ListSelectionListener() {
      
            public void valueChanged(ListSelectionEvent e) {
              if (!e.getValueIsAdjusting()) {
                panel.setBackground(colorMap.get((String)list.getSelectedValue()));
              }
            }
          });
          list.setSelectedIndex(0);
          panel.add(new JScrollPane(list));
          JFrame frame = new JFrame();
          frame.add(panel);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.setSize(300, 300);
          frame.setLocationRelativeTo(null);
          frame.setVisible(true);
        }
      }
      Thank you for reading this and for your advice.

      db
        • 1. Re: Any way to get the Class from a generic Type?
          852060
          Darryl Burke wrote:
          I wanted to male a generic class to map static fields of a class, of the same type (think <tt>Color.RED</tt>) and the only way I could find to obtain the Class/Fields was by passing the class literal to the constructor. Is there a way to make this with a no-argument constructor?
          Well, you need to know the class somehow. Alternatively you could force reification with an anonymous inner class, e.g. new StaticFieldMap<Color>() {}. This would let StaticFieldMap inspect its own j.l.r.Type to figure out the type argument. I will say that — not knowing your exact requirements — I myself would probably go with a static factory method.

          On an unrelated note: Favor composition over inheritance; StaticFieldMap should implement Map and delegate to a HashMap internally.

          With kind regards
          Ben
          • 2. Re: Any way to get the Class from a generic Type?
            Darryl Burke
            Thanks Ben. Sorry -- what's a j.l.r. type?

            I don't really have any 'requirements' being just a hobby programmer. But the other day I wrote a program to display all the 'named colors' of javafx.scene.paint.Color (there are many more than in java.awt.Color) and I felt that a utility class/method might be handy to have around.
            Favor composition over inheritance; StaticFieldMap should implement Map and delegate to a HashMap internally.
            Shall do that, thanks. I guess your reasoning behind the advice is that the class<tt> is-a </tt>Map but<tt> is-not-a </tt>HashMap. Am i right?

            Or do you think it would be better/cleaner to do this with a utility method?
              @SuppressWarnings("unchecked")
              public static <T> Map<String, T> getFieldMap(Class<T> clazz) {
                Map<String, T> map = new HashMap<String, T>();
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                  if (field.getType() == clazz) {
                    try {
                      map.put(field.getName(), (T) field.get(null));
                    } catch (IllegalArgumentException ex) {
                      ex.printStackTrace();
                    } catch (IllegalAccessException ex) {
                      ex.printStackTrace();
                    }
                  }
                }
                return map;
              }
            Thanks again, db
            • 3. Re: Any way to get the Class from a generic Type?
              852060
              Darryl Burke wrote:
              Thanks Ben. Sorry -- what's a j.l.r. type?
              A java.lang.reflect.Type, any j.l.Class implements that interface. What you would need to do is class this.getClass().getGenericSuperclass(), cast the returned Type to ParameterizedType, call getActualTypeArguments() and the result would be the class object of Color.
              I don't really have any 'requirements' being just a hobby programmer. But the other day I wrote a program to display all the 'named colors' of javafx.scene.paint.Color (there are many more than in java.awt.Color) and I felt that a utility class/method might be handy to have around.
              Well, the technique outlined above is somewhat fragile, but workable. If it's "just" for a pet project you will have to decide for yourself whether it is the right fit.
              Favor composition over inheritance; StaticFieldMap should implement Map and delegate to a HashMap internally.
              Shall do that, thanks. I guess your reasoning behind the advice is that the class<tt> is-a </tt>Map but<tt> is-not-a </tt>HashMap. Am i right?
              Correct, and thus implementation details would be leaking into your public API if it were to inherit from HashMap.
              Or do you think it would be better/cleaner to do this with a utility method?
              Yes. However, playing around with the j.l.r.Type API is a good learning experience. I remember trying to seriously use it and then thinking better of it. Nonetheless there are use cases where it makes sense [1].

              With kind regards
              Ben

              [1] http://code.google.com/p/google-guice/wiki/FrequentlyAskedQuestions#How_to_inject_class_with_generic_type?
              • 4. Re: Any way to get the Class from a generic Type?
                Darryl Burke
                Is this what you meant?
                import java.lang.reflect.Field;
                import java.lang.reflect.ParameterizedType;
                import java.lang.reflect.Type;
                import java.util.Collection;
                import java.util.HashMap;
                import java.util.Map;
                import java.util.Map.Entry;
                import java.util.Set;
                
                public abstract class StaticFieldMap<T> implements Map<String, T> {
                
                  private final Map<String, T> content = new HashMap<String, T>();
                
                  @SuppressWarnings("unchecked")
                  protected StaticFieldMap() {
                    Type superType = getClass().getGenericSuperclass();
                    if (superType instanceof ParameterizedType) {
                      ParameterizedType paraType = (ParameterizedType) superType;
                      Class clazz = (Class) paraType.getActualTypeArguments()[0];
                      Field[] fields = clazz.getDeclaredFields();
                      for (Field field : fields) {
                        if (field.getType() == clazz) {
                          try {
                            put(field.getName(), (T) field.get(null));
                          } catch (IllegalArgumentException ex) {
                            ex.printStackTrace();
                          } catch (IllegalAccessException ex) {
                            ex.printStackTrace();
                          }
                        }
                      }
                    }
                  }
                
                  public int size() {
                    return content.size();
                  }
                
                  public boolean isEmpty() {
                    return content.isEmpty();
                  }
                
                  public boolean containsKey(Object key) {
                    return content.containsKey(key);
                  }
                
                  public boolean containsValue(Object value) {
                    return content.containsValue(value);
                  }
                
                  public T get(Object key) {
                    return content.get(key);
                  }
                
                  public T put(String key, T value) {
                    return content.put(key, value);
                  }
                
                  public T remove(Object key) {
                    return remove(key);
                  }
                
                  public void putAll(Map<? extends String, ? extends T> m) {
                    content.putAll(m);
                  }
                
                  public void clear() {
                    content.clear();
                  }
                
                  public Set<String> keySet() {
                    return content.keySet();
                  }
                
                  public Collection<T> values() {
                    return content.values();
                  }
                
                  public Set<Entry<String, T>> entrySet() {
                    return content.entrySet();
                  }
                }
                Usage in the SSCCE posted earlier:
                final Map<String, Color> colorMap = new StaticFieldMap<Color>(){};
                db
                • 5. Re: Any way to get the Class from a generic Type?
                  852060
                  Darryl Burke wrote:
                  Is this what you meant?
                  That does look correct.

                  With kind regards
                  Ben
                  • 6. Re: Any way to get the Class from a generic Type?
                    Darryl Burke
                    Thank you, Ben. Thread marked 'Answered'

                    db