Ответ 1
Когда вы вызываете returnNull
компилятор неявно делает это:
Arrays.asList(new Object[]{ returnNull() });
Таким образом, разница в том, что массив равен нулю и допустимый массив с одним нулевым элементом.
Эта маленькая программа
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;
}
не. Почему они ведут себя по-разному?
Когда вы вызываете returnNull
компилятор неявно делает это:
Arrays.asList(new Object[]{ returnNull() });
Таким образом, разница в том, что массив равен нулю и допустимый массив с одним нулевым элементом.
Разница только в том, как аргумент используется во время выполнения:
Подпись 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.
Изучив сгенерированный 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
Очень просто, когда вы передаете 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
элементы
Подпись asList(): - public static <T> List<T> asList(T... a)
Так что args требует Array типа T.
1-й случай: когда вы кладете null
как arg в asList, массив будет указывать на null, следовательно, выбрасывает исключение
2-й случай: когда вы возвращаете ссылку на любой объект, который указывает на null
. Тогда это означает, что массив имеет один объект, и этот объект указывает на null
поэтому не вызывает исключение.
Подпись Array.asList
является
public static <T> List<T> asList(T... a)
где T
- ограничение типа. Обозначение T... a
является просто синтаксическим сахаром для массива T[]
. Так что же это за T
?
В вашем втором фрагменте это ясно, T
- Object
поэтому аргумент - Object[]
.
Но что такое T
в вашем первом фрагменте? Нет никакого способа узнать это => NPE