在Java中,相同类型但不同类型参数的空集合总是相等吗?

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由于运行时类型擦除(即类型参数StringInteger只能在编译时访问,但不能在编译时访问), 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。(这不能编译,但也许类似的事情是可能的。)

qqi*_*ihq 5

正如您所说,在给定示例中,类型信息在运行时丢失。但关于您在评论中提出的问题:

您还可以展示一个反例,即不同泛型类型的两个空实例不会被视为相等的集合吗?

您当然可以实现自己的,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)

  • 简短而否定的答案:**没有**显式类型标记是不可能的。“List&lt;T&gt;”在运行时实际上只是一个“List”,因此信息不存在。对于 `Class&lt;T&gt;` 中的类型是不同的,因为这是一个“一等公民”——即使它在运行时只是 `Class`,它仍然会保存提供的类型信息(例如 `String.class `,……) (2认同)