Java泛型 - 格式化程序的映射

时间:2014-04-22 08:26:39

标签: java generics

Formatter<T>知道如何将T格式化为字符串:

public interface Formatter<T> {
  String format(final T t);
}

我希望有Map个格式化程序,一个用于Integer,一个用于Date等:

Map<Class, Formatter> formatters;

预期用途是:

formatters.put(Integer.class, new Formatter<Integer>() {
    public String format(Integer i) {
      return i.toString;
    }
});

是否有某种方法可以强制键值在Class类型上达成一致?如果我说put(Integer.class, new Formatter<Integer>(){...})它会起作用,但put(Integer.class, new Formatter<Date>(){...})不会吗?

我现在正在尝试使用的是什么?作为类型:

Map<Class<?>, Formatter<?>> formatters;

但是我不能在这张地图中使用格式化程序:

Object obj = Integer.valueOf(15);
formatters.get(obj.getClass()).format(obj);
Error: The method format(capture#3-of ?) in the type Formatter<capture#3-of ?> is not applicable for the arguments (Object)

欢迎任何澄清。

3 个答案:

答案 0 :(得分:1)

由于泛型擦除,在语言层面无法做到这一点。您无法在运行时访问泛型类型。 (我不确定我是否正确使用了所有条款) 问题是,如果你传递Integer.class,那么需要在运行时评估应该有Formatter<Integer>。 Java中的泛型仅用于在编译期间强制执行类型检查,并在运行时“擦除”。 有一些方法可以在运行时访问泛型类型,但这有点像使用反射的黑客攻击。

它不会像你想象的那样奏效。

How to get a class instance of generics type T

答案 1 :(得分:1)

不幸的是,您无法在没有某些先前声明的<T>泛型类型的情况下在键和值之间创建此类关系。此类型也不应该为类修复,因此它应该能够在每个put调用中进行更改。

如果这是一个选项,请考虑使用泛型类型的方法,如

@SuppressWarnings("unchecked")
public static <T> void putSafe(Class<T> key, Formatter<T> value) {
    formatters.put(key, (Formatter<Object>) value);
}

会将一些值放入地图中,例如可能是您班级中的私人字段

private static Map<Class<?>, Formatter<Object>> formatters 
                  = new HashMap<Class<?>, Formatter<Object>>();

此外,您无法使用format引用中的Formatter<?>方法。这种引用可以指向Formatter<Integer>Formatter<Date>或任何其他类型的Formatter,编译器将无法确定您使用的是哪种,因此您可能会使用Formatter<Date>Integer对象上(同样的问题,为什么Java不允许您在add引用中使用List<?>方法 - 因为我们不知道哪种类型的列表引用允许执行{ {1}}如果列入实际的苹果列表add(new Banana)

,可能会导致问题

要解决此问题,您可以明确说明地图将存储List<Apple>,并且由于Formatter<Object>只要扩展对象就能接受任何类型的数据。这种方法的唯一问题是format无法引用Formatter<Object> [1],因此您必须明确地将传递的格式化程序转换为Formatter<Integer>,这通常可能不安全[2]并且你会被编译器警告它,但在这种情况下你不应该没事,所以你可以抑制这个警告。


[1]与Formatter<Object>类似,无法引用List<Fruit>,因为通过水果列表,您可以将List<Apple>添加到Banana <列表中登记/> [2]将Apples转换为List<Apple>通常不是最好的主意,编译器会警告我们这种方法

答案 2 :(得分:1)

我的解决方案需要一个抑制警告,但它会确保,泛型类型是相同的:

public class Example {

    private static Map<Class<?>, Formatter<?>> FORMATTERS =
            synchronizedMap(new HashMap<Class<?>, Formatter<?>>());

    public static <T> void addFormatter(Class<T> clazz, Formatter<T> formatter) {
        FORMATTERS.put(clazz, formatter);
    }
}

interface Formatter<T> {
    String format(final T value);
}

更新:我添加了Pshemo的解决方案,它首先给了我一个编译错误,但现在有效。好奇,但是。上面显示的代码现在是两个解决方案的合并,它删除了丑陋的@SuppressWarnings("unchecked")