Can generics allow the Java compiler to check the type of keys and values โ€‹โ€‹in a map?

I'm in a situation where I want to have a map where the keys are the class of the interface and the corresponding value is the class that implements that interface. In other words, key and value type are related.

My current implementation of a method that is added to the map and gets an instance of the implementation class looks like this:

// should be something like Class<T>, Class<? extends T>
static Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>> ();

public static <T> void add(Class<T> interfaceT,
     Class<? extends T> implementationT) {

  map.put(interfaceT, implementationT);
}

public static <T> T get(Class<T> interfaceT) {
  // cast caused by definition not complete.

  Class<T> implementationT = (Class<T>) map.get(interfaceT);

  // try catch stuff omitted
  T t = implementationT.newInstance();
  return t;
 }

      

My question is:

Can I define a "map" variable to prevent the get (...) method from being applied? I couldn't create a "new job HashMap<Class<T>, Class<? extends T>>

()", so either this is not possible or I missed something fundamental :)

Please inform :)


Edit: It turned out that the asSubclass () method in the class did what I wanted: D

Class<?> rawClassFromMap = map.get(interfaceT);
Class<? extends T> implementationT = rawClassFromMap.asSubclass(interfaceT);

      

It's good that implementationT is of type "? Extends T", since I only want the returned object T.

I like generics. Reminds me of Haskell ...

+2


source to share


2 answers


It looks like the target is something like the "Typical Heterogeneous Container" described by Josh Bloch in Chapter 5 of Effective Java (point 29). In his case, it maps type ( Class<T>

) to instance (already instantiated) ( T

).

You can do something like this using asSubclass

instead cast

:



final class Factory
{

  private Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();

  <T> void map(Class<T> type, Class<? extends T> impl)
  {
    map.put(type, impl.asSubclass(type));
  }

  private <T> Class<? extends T> get(Class<T> type)
  {
    Class<?> impl = map.get(type);
    if (impl == null) 
      throw new IllegalArgumentException("Unknown type: " + type);
    return impl.asSubclass(type);
  }

  <T> T create(Class<T> type) 
    throws Exception
  {
    Class<? extends T> impl = get(type);
    Constructor<? extends T> ctor = impl.getConstructor();
    return ctor.newInstance();
  }

}

      

+4


source


I would suggest Proxy . Here's a Java example .



public interface Bike {

    public String getWheels();

    public int getSize();

}

public class MountainBike implements Bike {

    @Override
    public int getSize() {
        return 24;
    }

    @Override
    public String getWheels() {
        return "Treaded";
    }

    @Override
    public String toString() {
        String newLine = System.getProperty("line.separator");
        StringBuilder sb = new StringBuilder();
        sb.append("Type:   MOUNTAIN").append(newLine);
        sb.append("Wheels: ").append(getWheels()).append(newLine);
        sb.append("Size:   ").append(getSize()).append(newLine);
        return sb.toString();
    }

}

public class CruiserBike implements Bike {

    @Override
    public int getSize() {
        return 26;
    }

    @Override
    public String getWheels() {
        return "Smooth";
    }

    @Override
    public String toString() {
        String newLine = System.getProperty("line.separator");
        StringBuilder sb = new StringBuilder();
        sb.append("Type:   CRUISER").append(newLine);
        sb.append("Wheels: ").append(getWheels()).append(newLine);
        sb.append("Size:   ").append(getSize()).append(newLine);
        return sb.toString();
    }

}

public class BikeProxy implements InvocationHandler {

    private Object obj;

    public static Object newInstance(Object obj) 
    {
        return java.lang.reflect.Proxy.newProxyInstance(obj.getClass()
                .getClassLoader(), obj.getClass().getInterfaces(),
                new BikeProxy(obj));
    }

    public static <T> T newInstance(String className) 
    {
        try 
        {
            return (T) newInstance(Class.forName(className));
        } 
        catch (ClassNotFoundException e) 
        {
            throw new RuntimeException(e);
        }
    }

    public static <T> T newInstance(Class<T> bikeClass) 
    {
        try
        {
        return (T) java.lang.reflect.Proxy.newProxyInstance(Bike.class.getClassLoader(), new Class[]{Bike.class},
                new BikeProxy(bikeClass.newInstance()));
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }

    private BikeProxy(Object obj) 
    {
        this.obj = obj;
    }

    public Object invoke(Object proxy, Method m, Object[] args)
            throws Throwable 
    {
        Object result;
        try 
        {
            result = m.invoke(obj, args);
        } 
        catch (InvocationTargetException e) 
        {
            throw e.getTargetException();
        } 
        catch (Exception e) 
        {
            throw new RuntimeException(e);
        }
        return result;
    }
}

public class ProxyTester 
{
    public static void main(String[] args) 
    {
        Bike mountainBike = BikeProxy.newInstance(MountainBike.class);
        System.out.println(mountainBike);

        Bike mountainBike2 = BikeProxy.newInstance(MountainBike.class.getName());
        System.out.println(mountainBike2);

        Bike cruiserBike = BikeProxy.newInstance(CruiserBike.class);
        System.out.println(cruiserBike);

        Bike cruiserBike2 = BikeProxy.newInstance(CruiserBike.class.getName());
        System.out.println(cruiserBike2);
    }
}

      

0


source







All Articles