泛型方法的泛型返回类型

时间:2015-02-17 09:49:59

标签: java generics type-erasure

考虑这样的通用方法定义:

public static <T> T getParameter(final Context context, final ContextParameter contextParameter, final Class<T> clazz) {
    return (T) context.get(contextParameter.name());
}

在您尝试像List&lt; String&gt;这样的通用对象之前,可以完美地获取任何类型的对象。我能让它发挥作用的唯一方法就是这样:

 final List<String> fileNames = 
             (List<String>) ContextUtils.getParameter(context,
                             ContextParameter.EOF_FILE_NAMES_TO_ZIP, List.class);

但是这需要额外的演员,取消了使用该方法的目的。有没有办法能够提供List<String>.class或类似的方法? (我知道类型擦除会阻止List<String>存在,但也许有办法实现它。)

注意:使其工作我的意思是没有类型安全警告。我知道代码会按原样运行,但它不是类型安全的。

2 个答案:

答案 0 :(得分:7)

您无法在不修改下游代码(context.get())的情况下以类型安全的方式执行此操作,因为通用类型在运行时被删除。实际上,目前clazz参数根本没有被使用:你可以简单地删除那个参数,并且该方法的执行方式相同,因为转换为(T)目前是无操作的,因为擦除。即使对于非genric类,您当前的代码也不是类型安全的。

因此,如果您不关心安全性,并且只是想避免在客户端代码中进行强制转换,则可以删除最后一个参数,并在方法上禁止一次。

如果你想要类型安全,你可以使用超类型令牌来保存泛型类型信息:Guava&#39; TypeToken可以工作并且很容易获得(IMO每个Java项目都应该使用Guava)。

但是,它需要对代码进行下游更改,因为您需要在添加对象时捕获类型信息,并在它们出现时进行检查。你没有分享Context课程的代码,所以很难说这是否可能,但无论如何你都想要这样的东西:< / p>

static class TypedParam {
    final TypeToken<?> type;
    final Object object;
    private TypedParam(TypeToken<?> type, Object object) {
        this.type = type;
        this.object = object;
    }
}

public static class Context {
    Map<String, TypedParam> params = new HashMap<>();
    TypedParam get(String name) {
        return params.get(name);
    }
}

public static <T> T getParameter(final Context context, final String name, final TypeToken<T> type) {
    TypedParam param = context.get(name);
    if (type.isAssignableFrom(param.type)) {
        @SuppressWarnings("unchecked")
        T ret = (T)param.object;
        return ret;
    } else {
        throw new ClassCastException(param.type + " cannot be assigned to " + type);
    }
}

基本上你知道参数图中所有对象的泛型类型,并检查&#34;通用感知转换&#34;在运行时。我上面没有包含Context.put(),但您在添加参数时需要捕获类型信息。

你仍然有一个@SuppressWarnings("unchecked"),但在这里它可以证明是类型安全的,因为你正在维护类型信息(在许多通用类的内容中,你会找到可证明安全的类型,所以即使在正确的代码中也无法避免它们。)

答案 1 :(得分:1)

如你所说,没有像List<String>.class这样的东西。因此,如果您只想读取没有强制转换的参数,可以使用以下内容:

public static <T> T getParameter(final Map<String, Object> context, final String name) {
    return (T) context.get(name);
}

我假设你的上下文也返回Object,你需要为这一种方法抑制类型安全检查。