Почему Arrays.asList(null) генерирует исключение NullPointerException, а Arrays.asList(someNullVariable) - нет?

Эта маленькая программа

public class Client {
    public static void main(String[] args) throws Exception {
        Arrays.asList(null);
    }
}

NullPointerException.

Exception in thread "main" java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:221)
    at java.base/java.util.Arrays$ArrayList.<init>(Arrays.java:4322)
    at java.base/java.util.Arrays.asList(Arrays.java:4309)
    at org.example.Client.main(Client.java:10)

Эта программа, однако,

public static void main(String[] args) throws Exception {
    Arrays.asList(returnNull());
}

private static Object returnNull(){
    return null;
}

не. Почему они ведут себя по-разному?

Ответ 1

Когда вы вызываете returnNull компилятор неявно делает это:

Arrays.asList(new Object[]{ returnNull() });

Таким образом, разница в том, что массив равен нулю и допустимый массив с одним нулевым элементом.

Ответ 2

Разница только в том, как аргумент используется во время выполнения:

Подпись asList

public static <T> List<T> asList(T... a)

Arrays.asList(returnNull()) вызывает его с помощью Object. Это явно не интерпретируется как массив. Java создает массив во время выполнения и передает его как массив с одним null элементом. Это эквивалентно Arrays.asList((Object) null)

Тем не менее, когда вы используете Arrays.asList(null), переданный аргумент принимается за массив, и, поскольку метод явно не работает с пустыми массивами, переданными в качестве аргумента (см. java.util.Arrays.ArrayList.ArrayList(E[])), вы получите этот NPE.

Ответ 3

Изучив сгенерированный bytecode мы видим, что во втором примере создается новый array:

public static main([Ljava/lang/String;)V
  L0
    LINENUMBER 8 L0
    ICONST_1
    ANEWARRAY java/lang/Object // <---
    DUP
    ICONST_0
    INVOKESTATIC .../.../Client.returnNull ()Ljava/lang/Object;
    AASTORE
    INVOKESTATIC java/util/Arrays.asList ([Ljava/lang/Object;)Ljava/util/List;
    POP

В то время как в первом примере используется простая null ссылка - отсюда и NPE:

public static main([Ljava/lang/String;)V
  L0
    LINENUMBER 8 L0
    ACONST_NULL // <---
    INVOKESTATIC java/util/Arrays.asList ([Ljava/lang/Object;)Ljava/util/List;
    POP

Ответ 4

Очень просто, когда вы передаете null, этот null интерпретируется как массив. Это так же, как когда вы явно его разыгрываете:

Arrays.asList((Object[]) null); // throws

Или когда ты пишешь:

Object[] array = null;
Arrays.asList(array); // throws

В то время как в вашем втором фрагменте, поскольку вы возвращаете Object компилятор обрабатывает его как массив одного элемента, так же, как когда вы приводите null напрямую к Object:

Arrays.asList((Object) null); // works

Или же:

Object object = null;
Arrays.asList(object); // works

Или же:

Object array = {null};
Arrays.asList(array); // works

asList есть ограничения для передачи в null -arrays, но нет ни одного, чтобы защитить вас от передачи в массивы, которые содержат null элементы

Ответ 5

Подпись asList(): - public static <T> List<T> asList(T... a)

Так что args требует Array типа T.

1-й случай: когда вы кладете null как arg в asList, массив будет указывать на null, следовательно, выбрасывает исключение
2-й случай: когда вы возвращаете ссылку на любой объект, который указывает на null. Тогда это означает, что массив имеет один объект, и этот объект указывает на null поэтому не вызывает исключение.

Ответ 6

Подпись Array.asList является

public static <T> List<T> asList(T... a)

где T - ограничение типа. Обозначение T... a является просто синтаксическим сахаром для массива T[]. Так что же это за T?

В вашем втором фрагменте это ясно, T - Object поэтому аргумент - Object[].

Но что такое T в вашем первом фрагменте? Нет никакого способа узнать это => NPE