Java 如何对具有不同类的类似方法使用泛型

Java 如何对具有不同类的类似方法使用泛型,java,generics,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 g

有没有办法使用一些通用方法来统一下面类似的方法

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中优雅地完成。

< p>因为泛型类型的类型删除,在运行时不能创建泛型类型的新实例。 如果您使用的是Java8,那么您可以让方法获取一个供应商,该供应商为您提供一个新对象。这使得调用方有责任创建对象。您可以这样做:

Map<String, Object> map = new HashMap<>();

public <T> T getInstance(String key, Supplier<T> objectSupplier) {
    if (instanceAMap.containsKey(key)) {
        return (T) map.get(key);
    }

    T result = objectSupplier.get();
    map.put(key, result);
    return result;
}
由于未经检查的强制转换,以后不要执行此操作:

Integer i = getInstance("String", () -> 5);
它将导致ClassCastException

编辑:这是一个为每种类型创建新地图的版本

Map<Class<?>, Map<String, ?>> maps = new HashMap<>();

public <T> T getInstance(String key, Class<T> type, Supplier<T> objectSupplier) {

    Map<String, T> map;
    if (maps.containsKey(type)) {
        map = (Map<String, T>) maps.get(type);
    } else {
        map = new HashMap<>();
        maps.put(type, map);
    }

    if (map.containsKey(key)) {
        return map.get(key);
    }

    T result = objectSupplier.get();
    map.put(key, result);
    return result;
}
Map>maps=newhashmap();
public T getInstance(字符串键、类类型、供应商对象供应商){
地图;
if(地图容器(类型)){
map=(map)maps.get(type);
}否则{
map=新的HashMap();
地图。放置(类型,地图);
}
if(地图容器(图例)){
返回map.get(key);
}
T result=objectSupplier.get();
地图。放置(键、结果);
返回结果;
}

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

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);
}

private MAPI是否确定所有这些类都有一个可访问的无参数构造函数?您的类为什么拥有3个不同的映射?你能重构它并拥有一个映射吗?@Seelenvirtuose不,类可能有任何构造函数,这只是一个例子。@user902383类的键可能不是唯一的。不幸的是,这些方法中的每一个都在另一个映射上操作。这也必须解决。哦,我没看到。我想我们也需要映射作为参数。此外,在Java-8之前的版本中,您可以使用类型标记:
T getInstance(字符串键,类类型)
。方法实现还可以使用此类型生成实例:
type.newInstance()
。不,可以在方法所属的实例中维护映射。使用类型标记(见上文),您还可以获得正确的映射。如果您相应地更新了您的问题,我将删除我的下一票。@请参见LenVirtuose如何使用类型标记检索正确的映射?在第一个示例中,使用值类型为
Object
的instanceMap会丢失类型信息。在上一个示例中,您使用
instanceType.cast(value)
来转换值。这并不比使用
(T)值来强制转换值更安全。您将丢失编译器警告,但它仍然可以抛出ClassCastException。重载的例子看起来不错。@marstran是的,如果没有方法重载,一些强制转换是不可避免的。但这样,就没有什么意外了:ClassCastException发生在一个明显的地方,如果它发生的话。另一方面,当您执行未经检查的强制转换时,您会在代码的后面的意外位置得到ClassCastException。例如,将一个包含整数的原始列表强制转换为
List
,将在稍后迭代或调用get(int)时导致意外异常。
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);
}
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);
}
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);
}
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);
    }
}