This discussion is archived
5 Replies Latest reply: May 25, 2010 12:02 PM by 843793 RSS

A list of homogeneous generic lists. Can't use homogeneous list methods (?)

843793 Newbie
Currently Being Moderated
It's hard to explain my problem in words. I document it much better in the test/example code. But I'll take a swing at it anyways.

Basically, I have a list (1) of lists (2). Each inner list (2) is generic (much like ArrayList), but obviously contains "homogeneous" data, or rather, data each being an instance of the same class. The next list (2) will use a different class. The problem arises when I try to access one of those inner lists (2) from the outer list (1), at which point the type of the list is unknown <?>, and trying to access methods which use that type, e.g. add(?) are unusable.

The code below tries to catch the gist of what I'm ultimately trying to achieve, to demonstrate that I am using generics in a useful way, and to show the specific problem that I encounter that I am trying to overcome, as well as a hacked workaround using reflection which I'm currently using but desperately want to get rid of. As a result, it's a little lengthy, but a lot of it is just comments.

The code has enough comments to explain itself.
import java.util.ArrayList;
import java.util.TreeMap;

public class TestCase
     {
     //approximately 15 classes extend this class.
     abstract class Resource //in my actual implementation, this is also a generic, but I stripped it for brevity
          {
          //contains some abstract methods
          }

     class ResourceList<R extends Resource> extends ArrayList<R>
          {
          private static final long serialVersionUID = 1L;

          /** constructs a new resource of the list's type, adds it to this list, and returns it */
          R add()
               {
               //method stripped for brevity
               return null;
               }

          /** Creates a duplicate in this list and returns it. */
          R duplicate(R res)
               {
               //method stripped for brevity.
               return res;
               }
          }

     //this is an example of a class that might extend Resource
     class Gold extends Resource
          {
          //contains various properties of this instance of gold, such as perhaps carat and quantity
          //also implements the abstract methods in Resource
          }

     /**
      * Changed to ArrayList for simplicity's sake.<br />
      * This is a mapping of resource types (e.g. GOLD_LIST) to resource lists (e.g. lists.get(0)).
      * For example, the first list will contain all Gold. The second list will contain a different resource (e.g. all Wood), and so on.
      * The number of lists is finite and constant, but depending on in-program parameters, may vary by 1 or 2 at setup.
      */
     ArrayList<ResourceList<? extends Resource>> lists = new ArrayList<ResourceList<? extends Resource>>();

     //indexes Gold into the mapping
     static final int GOLD_LIST = 0;

     ////////////////////////////////////////////////////
     // The code below is largely for example purposes //
     ////////////////////////////////////////////////////

     void setup()
          {
          ResourceList<Gold> rl = new ResourceList<Gold>();
          lists.add(GOLD_LIST,rl);
          }

     //this method demonstrates the problems I'm experiencing.
     //In implementation, I have a method which "uses" similar code to the problem code below,
     //but it is much too complex to introduce in this test case.
     //Solve the problems below, and I can figure out the rest. 
     void test(int list)
          {
          //If I were to, for example, programmatically try to add Gold to its respective list, here's what happens:
          Gold g = new Gold();
          lists.get(GOLD_LIST).add(g);
          //^ Error: The method add(capture#1-of ? extends TestCase.Resource) in the type
          //ArrayList<capture#1-of ? extends TestCase.Resource> is not applicable for the arguments (TestCase.Gold)

          //At this time, I don't actually know what kind of resource I'm dealing with.
          //Whereas normally I'd be able to access the type of list (e.g. Gold List) explicitly,
          //here I have to access it programmatically (via the mapping).

          Resource res = lists.get(list).add();
          //^ works fine

          Resource r1 = lists.get(list).duplicate(res);
          //^ Error: The method duplicate(capture#3-of ? extends TestCase.Resource) in the type
          //TestCase.ResourceList<capture#3-of ? extends TestCase.Resource> is not applicable for the arguments (TestCase.Resource)

          //Current ugly-as-hell workaround that I want to get rid of
          ResourceList<?> rl = lists.get(list);
          Resource r2 = (Resource) rl.getClass().getMethod("duplicate",Resource.class).invoke(rl,res);
          }

     public static void main(String[] args)
          {
          TestCase tc = new TestCase();
          tc.setup();
          tc.test(GOLD_LIST);
          }
     }
The problem, as I already said, is that the generic type gets lost and becomes <?>, and you can't really pass an argument to a method that's expecting "?".
My question, then, is how would I access the duplicate method programmatically? Preferably some method that doesn't use the hacked reflection shown above.


Thanks in advance.

Edited by: IsmAvatar2 on May 25, 2010 12:37 PM
  • 1. Re: A list of homogeneous generic lists. Can't use homogeneous list methods (?)
    jtahlborn Expert
    Currently Being Moderated
    you can't do what you want, the main problem being that your top-level list is heterogeneous. the best you could do would be to declare:
    ArrayList<ResourceList<Resource>> lists = ...
  • 2. Re: A list of homogeneous generic lists. Can't use homogeneous list methods (?)
    843793 Newbie
    Currently Being Moderated
    In which case I can't add a ResourceList<Gold> to it. Doing so causes the setup() method to not compile saying so.
  • 3. Re: A list of homogeneous generic lists. Can't use homogeneous list methods (?)
    843793 Newbie
    Currently Being Moderated
    jtahlborn wrote:
    you can't do what you want, the main problem being that your top-level list is heterogeneous. the best you could do would be to declare:
    The issue also comes down to trying to assert run-time assumptions at compile-time. At compile time, who's to know that the second list is Gold typed? Why couldn't "somebody else" have set the second element to a List<Silver>?

    @OP, one way it is possible to get around this is to abandon the List interface and encapsulate the type-unsafety to a point where you can guarantee its runtime safety. Something like this:
    import java.util.ArrayList;
    import java.util.List;
    
    public class HeterogeneousList
    {
        private final List<Entry<?>> entries = new ArrayList<Entry<?>>();
    
        public <K> void add(Class<K> clazz, K value) {
         entries.add(new Entry<K>(clazz, value));
        }
        
        public <K> K get(Class<K> clazz, int index) {
         Entry<?> entry = entries.get(index);
         if ( clazz.isAssignableFrom(entry.clazz) ) {
             return clazz.cast(entry.data);
         }
            //or however you want to deal with this
         throw new IllegalArgumentException("You made a bad assumption");
        }
        
        private static class Entry<K> {
         private final Class<K> clazz;
         private final K data;
         public Entry(Class<K> clazz, K data) {
             this.clazz = clazz;
             this.data = data;
         }
        }
    }
    But why you would want a list structure at that point would be beyond me.
  • 4. Re: A list of homogeneous generic lists. Can't use homogeneous list methods (?)
    jtahlborn Expert
    Currently Being Moderated
    IsmAvatar2 wrote:
    In which case I can't add a ResourceList<Gold> to it. Doing so causes the setup() method to not compile saying so.
    of course. you would need to change the setup method to add a new ResourceList<Resource>. like i said, you can't really do what you want to do.
  • 5. Re: A list of homogeneous generic lists. Can't use homogeneous list methods (?)
    843793 Newbie
    Currently Being Moderated
    @endasil
    At compile time, who's to know that the second list is Gold typed? Why couldn't "somebody else" have set the second element to a List<Silver>?
    Ah, that's a very good point.
    But why you would want a list structure at that point would be beyond me.
    Actually we use a custom Map structure for the outer "list" now. I just made it an ArrayList for the test case because it's easier to understand.

    I'm looking into how that might be implemented, and if that can solve the problem right now. Will respond again once I get further along into it.