为什么从 Java 中的列表中删除原始类型时没有自动装箱?

Why no autoboxing when removing primitive type from a List in Java?

我有下面的代码 IndexOutOfBoundsException:

 List<Character> list = new ArrayList<>();
 char c  = 'a';
 list.add(c);
 list.remove(c); // gets fixed by passing list.remove((Character)c);

我知道发生这种情况是因为自动装箱不会在删除时发生,而是在添加元素时发生。我的问题是为什么?添加从 charCharacter 的自动装箱是可能的,而在 remove 方法中不是这样,有什么特别之处?

这实际上不是自动拆箱的问题,而是重载的问题:存在一种 List::remove(int)(按列表中的索引删除)方法,它比 List::remove(E) 更具体(通过使用 Object::equals).

搜索对象来删除

在您的情况下,您的 char 被转换为 int

add 的情况下,与使用索引删除的等效版本是 List::add(int, E)(请参阅 javadoc for details)。 List::add(E) 等同于 list.add(add(list.size(), E).

Java 将考虑将原始类型装箱到包装器类型,但前提是它尚未考虑不需要装箱参数的重载方法。

JLS, Section 15.12.2,覆盖选择的重载如下:

  1. The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.

This guarantees that any calls that were valid in the Java programming language before Java SE 5.0 are not considered ambiguous as the result of the introduction of variable arity methods, implicit boxing and/or unboxing. However, the declaration of a variable arity method (§8.4.1) can change the method chosen for a given method method invocation expression, because a variable arity method is treated as a fixed arity method in the first phase. For example, declaring m(Object...) in a class which already declares m(Object) causes m(Object) to no longer be chosen for some invocation expressions (such as m(null)), as m(Object[]) is more specific.

(大胆强调是我的)

  1. The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase.

This ensures that a method is never chosen through variable arity method invocation if it is applicable through fixed arity method invocation.

  1. The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.

编译器看不到 add 的其他重载可以在不装箱的情况下匹配 add(Character),因此它在第二阶段考虑装箱并找到它的匹配项。

但是,编译器确实发现 remove 的重载不带装箱匹配 remove(int),因此 char 被扩展为 int。编译器在第一阶段就找到了它的方法,所以从来没有考虑过第二阶段。 正如您已经发现的那样,这迫使您将 char 显式转换为 Character 以获得匹配的正确方法。

这恰好是 Java 5 中的情况,引入装箱和拆箱会导致在该版本之前不存在的歧义。如果在设计这些方法名称时考虑到这一点,设计者很可能会使用不同的方法名称,避免重载和此处的这种歧义。