来自泛型方法的泛型 return 类型
Generic return type from a generic method
考虑这样一个通用方法定义:
public static <T> T getParameter(final Context context, final ContextParameter contextParameter, final Class<T> clazz) {
return (T) context.get(contextParameter.name());
}
在您尝试像 List 这样的通用对象之前,它可以完美地获取任何类型的对象。我能让它工作的唯一方法是这样的:
final List<String> fileNames =
(List<String>) ContextUtils.getParameter(context,
ContextParameter.EOF_FILE_NAMES_TO_ZIP, List.class);
但这需要额外的转换,从而消除了拥有该方法的目的。有没有办法能够提供 List<String>.class
或类似方法的东西? (我知道类型擦除会阻止 List<String>
存在,但也许有办法做到这一点)。
注意:使它工作我的意思是没有类型安全警告。我知道代码可以按原样工作,但它不是类型安全的。
正如您所说,没有什么比 List<String>.class
更好的了。因此,如果您只想读取不带转换的参数,您可以使用这样的方法:
public static <T> T getParameter(final Map<String, Object> context, final String name) {
return (T) context.get(name);
}
我假设你的上下文 returns Object
以及你需要为那个方法禁止类型安全检查。
如果不修改下游代码 (context.get()
),您将无法以类型安全的方式执行此操作,因为通用类型在 运行 时被删除。事实上,目前根本没有使用 clazz
参数:您可以简单地删除该参数,并且该方法将执行相同的操作,因为由于擦除,转换为 (T)
目前是空操作。您当前的代码 即使对于非通用 classes 也是不安全的。
因此,如果您不关心安全性,只想避免在客户端代码中进行强制转换,您可以只删除最后一个参数,并在该方法上取消一次警告。
如果你想要类型安全,你可以使用超类型标记来保留通用类型信息:Guava 的 TypeToken
有效并且很容易获得(IMO 每个 Java 项目都应该已经使用 Guava)。
但是,这将需要对您的代码进行下游更改 - 因为您需要在添加对象时捕获类型信息并在对象出来时检查它。您没有共享 Context
class 的代码,所以很难说这是否可行,但无论如何您会想要这样的代码:
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);
}
}
基本上您知道参数映射中所有对象的通用类型,并在 运行 时用 "generic aware cast" 检查。我没有在上面包含 Context.put()
,但是您需要在添加参数时捕获类型信息。
你仍然有一个 @SuppressWarnings("unchecked")
,但这里它是可证明的类型安全的,因为你正在维护类型信息(在许多通用 classes 的内部你会发现可证明安全的转换,所以即使在正确的代码中也不能总是避免它们。
考虑这样一个通用方法定义:
public static <T> T getParameter(final Context context, final ContextParameter contextParameter, final Class<T> clazz) {
return (T) context.get(contextParameter.name());
}
在您尝试像 List
final List<String> fileNames =
(List<String>) ContextUtils.getParameter(context,
ContextParameter.EOF_FILE_NAMES_TO_ZIP, List.class);
但这需要额外的转换,从而消除了拥有该方法的目的。有没有办法能够提供 List<String>.class
或类似方法的东西? (我知道类型擦除会阻止 List<String>
存在,但也许有办法做到这一点)。
注意:使它工作我的意思是没有类型安全警告。我知道代码可以按原样工作,但它不是类型安全的。
正如您所说,没有什么比 List<String>.class
更好的了。因此,如果您只想读取不带转换的参数,您可以使用这样的方法:
public static <T> T getParameter(final Map<String, Object> context, final String name) {
return (T) context.get(name);
}
我假设你的上下文 returns Object
以及你需要为那个方法禁止类型安全检查。
如果不修改下游代码 (context.get()
),您将无法以类型安全的方式执行此操作,因为通用类型在 运行 时被删除。事实上,目前根本没有使用 clazz
参数:您可以简单地删除该参数,并且该方法将执行相同的操作,因为由于擦除,转换为 (T)
目前是空操作。您当前的代码 即使对于非通用 classes 也是不安全的。
因此,如果您不关心安全性,只想避免在客户端代码中进行强制转换,您可以只删除最后一个参数,并在该方法上取消一次警告。
如果你想要类型安全,你可以使用超类型标记来保留通用类型信息:Guava 的 TypeToken
有效并且很容易获得(IMO 每个 Java 项目都应该已经使用 Guava)。
但是,这将需要对您的代码进行下游更改 - 因为您需要在添加对象时捕获类型信息并在对象出来时检查它。您没有共享 Context
class 的代码,所以很难说这是否可行,但无论如何您会想要这样的代码:
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);
}
}
基本上您知道参数映射中所有对象的通用类型,并在 运行 时用 "generic aware cast" 检查。我没有在上面包含 Context.put()
,但是您需要在添加参数时捕获类型信息。
你仍然有一个 @SuppressWarnings("unchecked")
,但这里它是可证明的类型安全的,因为你正在维护类型信息(在许多通用 classes 的内部你会发现可证明安全的转换,所以即使在正确的代码中也不能总是避免它们。