Att*_*lio 2 java generics equals
考虑以下片段:
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
assertThat(list1.equals(list2), is(true));
Run Code Online (Sandbox Code Playgroud)
断言成功。
事实上,据我了解,equals由于运行时类型擦除(即类型参数String和Integer只能在编译时访问,但不能在编译时访问), Java 中的方法无法区分两者。在运行时)。由于没有可以比较的元素,因此equals必须返回 true。按照这个思路,对于所有集合来说,这一定是正确的。
所以问题是:我的思维过程是正确的,还是我遗漏了什么?
编辑
@qqilihq 给出了一个很好的答案,这引发了下一个问题:这是否可以在不显式传递类型的情况下实现(如果类型以某种方式显式存储,我很好,只是用户不必传递它。)
我尝试了以下操作,但它不起作用(我猜是由于类型擦除):
public TypedList(List<T> delegate) {
this.delegate = Objects.requireNonNull(delegate);
this.type = (Class<T>) delegate.getClass();
}
Run Code Online (Sandbox Code Playgroud)
但也许你可以做类似的事情?this.type = T。(这不能编译,但也许类似的事情是可能的。)
正如您所说,在给定示例中,类型信息在运行时丢失。但关于您在评论中提出的问题:
您还可以展示一个反例,即不同泛型类型的两个空实例不会被视为相等的集合吗?
您当然可以实现自己的,Collection它会考虑equals方法中的类型信息。TypedList这是一个围绕任意值创建包装器的非常简单的示例List:
static final class TypedList<T> extends AbstractList<T> {
private final List<T> delegate;
private final Class<T> type;
public TypedList(List<T> delegate, Class<T> type) {
this.delegate = Objects.requireNonNull(delegate);
this.type = Objects.requireNonNull(type);
}
@Override
public T get(int index) {
return delegate.get(index);
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean equals(Object obj) {
if (!super.equals(obj)) {
return false;
}
// Lists are equal, now additionally, check the type
TypedList<?> other = (TypedList<?>) obj;
return this.type.equals(other.type);
}
// hashCode omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)
请注意,由于提到的类型擦除,您需要显式存储类型。用法:
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.equals(list2)); // true
list1 = new TypedList<>(list1, String.class);
list2 = new TypedList<>(list2, Integer.class);
System.out.println(list1.equals(list2)); // false
Run Code Online (Sandbox Code Playgroud)