如何将泛型用于具有不同类的类似方法

时间:2015-07-15 15:59:46

标签: java generics

有没有办法使用某种通用方法来统一以下类似的方法?

public ClassA getInstanceA(String key)
{
   if (instanceAMap.contains(key))
   {
      return instanceAMap.get(key);
   }

   ClassA result = new ClassA();
   instanceAMap.put(key, result);
   return result;
}

public ClassB getInstanceB(String key)
{
   if (instanceBMap.contains(key))
   {
      return instanceBMap.get(key);
   }

   ClassB result = new ClassB();
   instanceBMap.put(key, result);
   return result;
}

public ClassC getInstanceC(String key)
{
   if (instanceCMap.contains(key))
   {
      return instanceCMap.get(key);
   }

   ClassC result = new ClassC();
   instanceCMap.put(key, result);
   return result;
}

所以,我想对所有类只有一种方法。 在C ++中,它可以包装成宏,但是如何在Java中优雅地完成它。

1 个答案:

答案 0 :(得分:0)

单个方法解决方案需要传入类型:

private Map<Class<?>, Map<String, Object>> instanceMaps;

public <T> T getInstance(String key,
                         Class<T> instanceType) {
    if (instanceMaps == null) {
        instanceMaps = new HashMap<>();
        instanceMaps.put(ClassA.class, instanceAMap);
        instanceMaps.put(ClassB.class, instanceBMap);
        instanceMaps.put(ClassC.class, instanceCMap);
    }

    Map<String, Object> instanceMap = instanceMaps.get(instanceType);
    if (instanceMap == null) {
        throw new IllegalArgumentException("Unknown type: " + instanceType);
    }

    Object value = instanceMap.get(key);
    if (value == null) {
        try {
            instanceMap.put(key, value = instanceType.newInstance());
        } catch (ReflectiveOperationException e) {
            throw new IllegalArgumentException(e);
        }
    }
    return instanceType.cast(value);
}

instanceAMap,instanceBMap和instanceCMap都必须声明为Map<String, Object>。无法从单个Map或Collection中检索不同的特定泛型类型。

就个人而言,我不会尝试强制使用单个方法,而是使用重载方法,可以将重构方法重构为单个私有泛型方法。这允许将instanceAMap声明为Map<String, ClassA>,依旧为instanceBMap和instanceCMap。

private <T> T getInstance(String key,
                          Map<String, T> instanceMap,
                          Supplier<T> constructor) {
    return instanceMap.computeIfAbsent(key, k -> constructor.get());
}

public ClassA getInstanceA(String key) {
    return getInstance(key, instanceAMap, ClassA::new);
}

public ClassB getInstanceB(String key) {
    return getInstance(key, instanceBMap, ClassB::new);
}

public ClassC getInstanceC(String key) {
    return getInstance(key, instanceCMap, ClassC::new);
}

在Java 8之前的版本中,上述内容可以写成:

private <T> T getInstance(String key,
                          Map<String, T> instanceMap,
                          Class<T> instanceType) {
    T value = instanceMap.get(key);
    if (value == null) {
        try {
            instanceMap.put(key, value = instanceType.newInstance());
        } catch (ReflectiveOperationException e) {
            throw new IllegalArgumentException(e);
        }
    }
    return value;
}

public ClassA getInstanceA(String key) {
    return getInstance(key, instanceAMap, ClassA.class);
}

public ClassB getInstanceB(String key) {
    return getInstance(key, instanceBMap, ClassB.class);
}

public ClassC getInstanceC(String key) {
    return getInstance(key, instanceCMap, ClassC.class);
}

正如@ user902383所说,如果你可以使用一个Map来保存所有的对象和键,那将是很有用的,但显然这需要保证没有两个类将使用相同的键:

public <T> T getInstance(String key,
                         Class<T> instanceType) {
    Object value = instanceMap.get(key);
    if (value != null) {
        return instanceType.cast(value);
    }

    try {
        T newValue = instanceType.newInstance();
        instanceMap.put(key, newValue);
        return newValue;
    } catch (ReflectiveOperationException e) {
        throw new IllegalArgumentException(e);
    }
}