title: Java: Creating objects with variable type and calling methods on an object with variable name
body: <p>I have a hashmap with keys of type String and values of type ArrayList.</p>
  
  <p>It looks something like this:</p>
  
  <pre><code>key: ".avi", value: {new VideoResource(var), "videoResources"}
  </code></pre>
  
  <p>I want to do the following things:</p>
  
  <p>1.
  Create a new variable with the same type as that of the one found in the hashmap. This time I would want to create a new VideoResource, but it could be anything (AudioResource, ImageResource...) depending on what's inside the hashmap. The types always take the same parameter "var", however.</p>
  
  <p>So, I want to do:</p>
  
  <pre><code>SomeResource resource = new SomeResource(var);
  </code></pre>
  
  <p>where SomeResource is decided by the type of some object.</p>
  
  <p>2.
  Call a method on an object, that is previously instantiated, with the name given by</p>
  
  <pre><code>String objectName = hashMap.get(key).get(1);
  </code></pre>
  
  <p>The object will always have the method and the method being called will always have the same name.</p>
  
  <p>So, I want to do:</p>
  
  <pre><code>objectName.methodName();
  </code></pre>
  
  <p>where objectName is decided by some string.</p>
  
  <p>How can I achieve this?</p>
  
  <p>EDIT</p>
  
  <p>The context is this:</p>
  
  <p>For an assignment I've been tasked to refactor a bit of code in the open-source project FreeCol. The method I am refactoring is createResource shown below. The problem with it is, that in addition to the very repetitive if-statements, it violates the open/closed principle. We want it to be open for extension, i.e. adding a new extension type (.avi etc.), but closed for modification, i.e. you shouldn't have to modify a whole bunch of code to do so.</p>
  
  <p>The method looks like this:</p>
  
  <pre><code>public static void createResource(URI uri, ResourceSink output) {
  if(findResource(uri, output))
      return;
  
  try {
      if ("urn".equals(uri.getScheme())) {
          if (uri.getSchemeSpecificPart().startsWith(ColorResource.SCHEME)) {
              ColorResource cr = new ColorResource(uri);
              output.add(cr);
              colorResources.put(uri, new WeakReference&lt;&gt;(cr));
          } else if (uri.getSchemeSpecificPart().startsWith(FontResource.SCHEME)) {
              FontResource fr = new FontResource(uri);
              output.add(fr);
              fontResources.put(uri, new WeakReference&lt;&gt;(fr));
          }
      } else if (uri.getPath().endsWith("\"")
              &amp;&amp; uri.getPath().lastIndexOf('"',
                      uri.getPath().length()-1) &gt;= 0) {
          StringResource sr = new StringResource(uri);
          output.add(sr);
          stringResources.put(uri, new WeakReference&lt;&gt;(sr));
      } else if (uri.getPath().endsWith(".faf")) {
          FAFileResource far = new FAFileResource(uri);
          output.add(far);
          fafResources.put(uri, new WeakReference&lt;&gt;(far));
      } else if (uri.getPath().endsWith(".sza")) {
          SZAResource szr = new SZAResource(uri);
          output.add(szr);
          szaResources.put(uri, new WeakReference&lt;&gt;(szr));
      } else if (uri.getPath().endsWith(".ttf")) {
          FontResource fr = new FontResource(uri);
          output.add(fr);
          fontResources.put(uri, new WeakReference&lt;&gt;(fr));
      } else if (uri.getPath().endsWith(".wav")) {
          AudioResource ar = new AudioResource(uri);
          output.add(ar);
          audioResources.put(uri, new WeakReference&lt;&gt;(ar));
      } else if (uri.getPath().endsWith(".ogg")) {
          if (uri.getPath().endsWith(".video.ogg")) {
              VideoResource vr = new VideoResource(uri);
              output.add(vr);
              videoResources.put(uri, new WeakReference&lt;&gt;(vr));
          } else {
              AudioResource ar = new AudioResource(uri);
              output.add(ar);
              audioResources.put(uri, new WeakReference&lt;&gt;(ar));
          }
      } else {
          ImageResource ir = new ImageResource(uri);
          output.add(ir);
          imageResources.put(uri, new WeakReference&lt;&gt;(ir));
      }
      catch (Exception e) {
          logger.log(Level.WARNING, "Failed to create resource with URI: " + uri, e);
      }
  }
  </code></pre>
  
  <p>So what I want to do, is to get rid of all the else ifs that handle the file extensions, and replace them with a single call to a method assignResource that creates the right resource</p>
  
  <pre><code>SomeResource resource = new SomeResource(uri);
  </code></pre>
  
  <p>adds it to the output</p>
  
  <pre><code>output.add(resource);
  </code></pre>
  
  <p>and puts it in the right WeakHashMap</p>
  
  <pre><code>someResource.put(uri, new WeakReference&lt;&gt;(resource));
  </code></pre>
  
  <p>These hashmaps are declared as following:</p>
  
  <pre><code>private static final Map&lt;URI, WeakReference&lt;ColorResource&gt;&gt; colorResources = new WeakHashMap&lt;&gt;();
  private static final Map&lt;URI, WeakReference&lt;FontResource&gt;&gt; fontResources = new WeakHashMap&lt;&gt;();
  private static final Map&lt;URI, WeakReference&lt;StringResource&gt;&gt; stringResources = new WeakHashMap&lt;&gt;();
  private static final Map&lt;URI, WeakReference&lt;FAFileResource&gt;&gt; fafResources = new WeakHashMap&lt;&gt;();
  private static final Map&lt;URI, WeakReference&lt;SZAResource&gt;&gt; szaResources = new WeakHashMap&lt;&gt;();
  private static final Map&lt;URI, WeakReference&lt;VideoResource&gt;&gt; videoResources = new WeakHashMap&lt;&gt;();
  private static final Map&lt;URI, WeakReference&lt;ImageResource&gt;&gt; imageResources = new WeakHashMap&lt;&gt;();
  </code></pre>
  
  <p>I have code that extracts the file extension from the URI, and puts it as a key, along with the associated object (i.e. VideoResource()) and the name of the object upon with we want to invoke the put() method (i.e. "videoResources"), as values.</p>
  
  <p>The idea is that if you would add a new extension type, you just have to do one function call to my method addResource:</p>
  
  <pre><code>addResource(resourceMap, ".ogg", new VideoResource(uri), "videoResources");
  </code></pre>
  
  <p>that adds these parameters to the map of file extensions it can handle.</p>
  
  <p>Instead of all the else if statements, a single call to a method assignResource</p>
  
  <pre><code>assignResource(uri, resourceMap);
  </code></pre>
  
  <p>would be made.</p>
  
  <p>So the problem I faced was how to create a new object of a type that matches that of the one found in my hashmap, and then invoking method put() on the right WeakHashMap (videoResources etc.).</p>
  
  <p>EDIT</p>
  
  <p>More questions.</p>
  
  <p>The line <code>output.add(resource);</code> gives an error because resource is a Resource and not one of the subtypes. The next line, <code>resources.add(uri, resource);</code> complains about type safety, that references should be parameterized. I changed the interface to your second suggested generic one. An example of what a Resources implementation now looks like is this:</p>
  
  <pre><code>class StringResources implements Resources&lt;StringResource&gt; {
  
      private final Map&lt;URI, WeakReference&lt;Resource&gt;&gt; resources = new WeakHashMap&lt;&gt;();
      @Override
      public boolean matches(URI uri) {
          return uri.getPath().endsWith("\"")
              &amp;&amp; uri.getPath().lastIndexOf('"', uri.getPath().length() - 1) &gt;= 0;
      }
  
      @Override
      public StringResource from(URI uri) {
          return new StringResource(uri);
      }
  
      @Override
      public void add(URI uri, StringResource resource) {
          resources.put(uri, new WeakReference&lt;&gt;(resource));
      }  
  }
  </code></pre>
  
  <p>Is this how you meant they should look? In that case, how should we change the lines</p>
  
  <pre><code>Resource resource = resources.from(uri);
  output.add(resource);
  resources.add(uri, resource);
  </code></pre>
  
  <p>so that the resource is of the right subtype when we call output.add?</p>