【发布时间】:2018-12-27 16:18:19
【问题描述】:
当泛型添加到 1.5 时,java.lang.reflect 添加了一个带有各种子类型的 Type 接口来表示类型。 Class 被改造为在 1.5 之前的类型中实现 Type。 Type 子类型可用于 1.5 中的新泛型类型。
这一切都很好。有点尴尬,因为Type 必须沮丧才能做任何有用的事情,但可以通过试验、错误、摆弄和(自动)测试来实现。除非涉及到实施......
equals和hashCode应该如何实现。 Type 的 ParameterizedType 子类型的 API 描述说:
实现此接口的类的实例必须实现 equals() 方法,该方法等同于任何两个共享相同泛型类型声明并具有相同类型参数的实例。
(我猜这意味着getActualTypeArguments 和getRawType 但不是getOwnerType??)
我们从java.lang.Object 的通用合同中知道,hashCode 也必须实现,但似乎没有说明该方法应该产生什么值。
Type 的其他子类型似乎都没有提到 equals 或 hashCode,除了 Class 的每个值都有不同的实例。
那么我在equals 和hashCode 中输入了什么?
(如果您想知道,我正在尝试将类型参数替换为实际类型。所以如果我知道 在运行时 TypeVariable<?> T 是 Class<?> String 那么我想要替换Types,所以List<T>变成List<String>,T[]变成String[],List<T>[](可能发生!)变成List<String>[],等等)
或者我是否必须创建自己的并行类型类型层次结构(出于假定的法律原因,不重复 Type)? (有图书馆吗?)
编辑:关于我为什么需要这个有几个疑问。确实,为什么要查看泛型类型信息?
我从非泛型类/接口类型开始。 (如果你想要一个参数化类型,例如List<String>,那么你总是可以添加一个带有新类的间接层。)然后我正在关注字段或方法。那些可能引用参数化类型。只要他们不使用通配符,当面对T 之类的东西时,我仍然可以计算出实际的静态类型。
通过这种方式,我可以使用高质量的静态类型来完成所有工作。这些instanceof 动态类型检查都看不到。
我的具体用法是序列化。但它可以适用于反射的任何其他合理使用,例如测试。
我用于以下替换的代码的当前状态。 typeMap 是 Map<String,Type>。呈现为“原样”快照。无论如何都没有整理(throw null;,如果你不相信我的话)。
Type substitute(Type type) {
if (type instanceof TypeVariable<?>) {
Type actualType = typeMap.get(((TypeVariable<?>)type).getName());
if (actualType instanceof TypeVariable<?>) { throw null; }
if (actualType == null) {
throw new IllegalArgumentException("Type variable not found");
} else if (actualType instanceof TypeVariable<?>) {
throw new IllegalArgumentException("TypeVariable shouldn't substitute for a TypeVariable");
} else {
return actualType;
}
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType)type;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
int len = actualTypeArguments.length;
Type[] actualActualTypeArguments = new Type[len];
for (int i=0; i<len; ++i) {
actualActualTypeArguments[i] = substitute(actualTypeArguments[i]);
}
// This will always be a Class, wont it? No higher-kinded types here, thank you very much.
Type actualRawType = substitute(parameterizedType.getRawType());
Type actualOwnerType = substitute(parameterizedType.getOwnerType());
return new ParameterizedType() {
public Type[] getActualTypeArguments() {
return actualActualTypeArguments.clone();
}
public Type getRawType() {
return actualRawType;
}
public Type getOwnerType() {
return actualOwnerType;
}
// Interface description requires equals method.
@Override public boolean equals(Object obj) {
if (!(obj instanceof ParameterizedType)) {
return false;
}
ParameterizedType other = (ParameterizedType)obj;
return
Arrays.equals(this.getActualTypeArguments(), other.getActualTypeArguments()) &&
this.getOwnerType().equals(other.getOwnerType()) &&
this.getRawType().equals(other.getRawType());
}
};
} else if (type instanceof GenericArrayType) {
GenericArrayType genericArrayType = (GenericArrayType)type;
Type componentType = genericArrayType.getGenericComponentType();
Type actualComponentType = substitute(componentType);
if (actualComponentType instanceof TypeVariable<?>) { throw null; }
return new GenericArrayType() {
// !! getTypeName? toString? equals? hashCode?
public Type getGenericComponentType() {
return actualComponentType;
}
// Apparently don't have to provide an equals, but we do need to.
@Override public boolean equals(Object obj) {
if (!(obj instanceof GenericArrayType)) {
return false;
}
GenericArrayType other = (GenericArrayType)obj;
return
this.getGenericComponentType().equals(other.getGenericComponentType());
}
};
} else {
return type;
}
}
【问题讨论】:
-
为什么需要添加equals/hashCode?你确定内置类型不正确吗?
-
@PeterLawrey 我需要检查类型是否匹配。当似乎没有规范时,内置实现怎么可能相等。我的意思是你可能可以通过观察来计算出实现的作用——这不是你要使用密码学的东西。
-
实现似乎有这些方法来自读取源代码。您可以尝试创建自己的系统,但这似乎需要做很多工作,因为它可能永远不需要。一个简单的解决方案是比较
toString()结果。 -
@PeterLawrey 我不确定
toString是否保证是唯一的。 (还有 bleurgh,真是个 hack。而且不适合我的HashMap。) -
另外,对递归类型要格外小心,即
<U extends Comparable<U>>
标签: java reflection