Vejamos o código Java simples no seguinte trecho:
public class Main {
private int temp() {
return true ? null : 0;
// No compiler error - the compiler allows a return value of null
// in a method signature that returns an int.
}
private int same() {
if (true) {
return null;
// The same is not possible with if,
// and causes a compile-time error - incompatible types.
} else {
return 0;
}
}
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.temp());
System.out.println(m.same());
}
}
Neste código Java mais simples, o temp()
método não emite nenhum erro do compilador, mesmo que o tipo de retorno da função seja int
, e estamos tentando retornar o valor null
(por meio da instrução return true ? null : 0;
). Quando compilado, isso obviamente causa a exceção do tempo de execução NullPointerException
.
No entanto, parece que a mesma coisa está errada, se nós representamos o operador ternário com uma if
declaração (como no same()
método), que faz emitir um erro de compilação! Por quê?
int foo = (true ? null : 0)
enew Integer(null)
ambos compilam bem, o segundo é a forma explícita de autoboxing.null
paraInteger
... Isso seria olhar apenas como "adivinhar" a mim ou "fazer as coisas funcionarem" ...Integer foo() { return "1"; }
não irá compilar.)Respostas:
O compilador interpreta
null
como uma referência nula aInteger
, aplica as regras de caixa automática / unboxing para o operador condicional (conforme descrito na Java Language Specification, 15.25 ) e segue em frente. Isso gerará umNullPointerException
tempo de execução, que você pode confirmar tentando.fonte
capture conversion
elub(T1,T2)
) ?? Além disso, é realmente possível aplicar boxe a um valor nulo? Isso não seria como "adivinhar"?lub(T1,T2)
é o tipo de referência mais específico em comum na hierarquia de tipos de T1 e T2. (Ambos compartilham pelo menos Object, por isso há sempre um tipo de referência mais específica.)null
não é encaixotado em um número inteiro, é interpretado como uma referência a um número inteiro (uma referência nula, mas isso não é um problema). Nenhum objeto Inteiro é construído a partir do nulo; portanto, não há motivo para uma NumberFormatException.null
(que não é um tipo numérico primitivo), a cláusula aplicável será "Se p for um valor de qualquer outro tipo, a conversão de boxe será equivalente a uma conversão de identidade. " Portanto, conversão de boxenull
paraInteger
yieldsnull
, sem chamar nenhumInteger
construtor.Eu acho que o compilador Java interpreta
true ? null : 0
como umaInteger
expressão, que pode ser implicitamente convertida emint
, possivelmente dandoNullPointerException
.Para o segundo caso, a expressão
null
é do tipo nulo especial , consulte , portanto, o códigoreturn null
cria incompatibilidade de tipo.fonte
true ? null : 0
comoInteger
? Autoboxing0
primeiro ??Na verdade, tudo está explicado na Especificação de Linguagem Java .
Portanto, o "nulo" no seu
(true ? null : 0)
obtém um tipo int e é autoboxado como Inteiro.Tente algo assim para verificar isso
(true ? null : null)
e você receberá o erro do compilador.fonte
int
valor da função, o que causa um NPE.null
paraInteger
comnew Integer(null);
"Let T1 ser o tipo que resulta da aplicação de conversão de boxe para S1 ..." você teria umNumberFormatException
e este não é o caso ...No caso da
if
declaração, anull
referência não é tratada comoInteger
referência porque não está participando de uma expressão que a força a ser interpretada como tal. Portanto, o erro pode ser facilmente capturado em tempo de compilação, porque é mais claramente um erro de tipo .Quanto ao operador condicional, a Especificação da Linguagem Java §15.25 "Operador Condicional
? :
" responde muito bem a isso nas regras de como a conversão de tipo é aplicada:fonte
0
for autoboxadoInteger
, o compilador está executando o último caso das "regras de operador ternárias", conforme descrito na Especificação de Linguagem Java. Se isso for verdade, é difícil para mim acreditar que ele passaria para o caso 3 das mesmas regras que possuem um tipo nulo e de referência que fazem com que o valor de retorno do operador ternário seja o tipo de referência (Inteiro). .Integer
? É exatamente o que está acontecendo; o NPE está sendo gerado tentando desmarcar o valor da expressão para retornar umint
da função Altere a função para retornar umInteger
e ele retornaránull
sem problemas.null
enquadre nessa categoria . Além disso, entraríamos na etapa "Caso contrário, a promoção numérica binária (§5.6.2) é aplicada ... Observe que a promoção numérica binária executa a conversão de conversão de unboxing (§5.1.8) ..." para determinar o tipo de retorno. Mas a conversão de unboxing geraria um NPE e isso acontece apenas em tempo de execução e não ao tentar determinar o tipo de operador ternário. Eu ainda estou confuso ..null
é tratado como se tivesse tipoint
, mas é realmente equivalente athrow new NullPointerException()
, só isso.A primeira coisa a ter em mente é que os operadores ternários Java têm um "tipo", e é isso que o compilador determinará e considerará, independentemente dos tipos reais / reais do segundo ou terceiro parâmetro. Dependendo de vários fatores, o tipo de operador ternário é determinado de diferentes maneiras, conforme ilustrado na Java Language Specification 15.26
Na pergunta acima, devemos considerar o último caso:
Esse é, de longe, o caso mais complexo, depois de aplicar a conversão de captura (§5.1.10) e, principalmente, em lub (T1, T2) .
Em inglês simples e após uma extrema simplificação, podemos descrever o processo como o cálculo da "Superclasse Menos Comum" (sim, pense no LCM) do segundo e terceiro parâmetros. Isso nos dará o operador ternário "tipo". Novamente, o que acabei de dizer é uma simplificação extrema (considere as classes que implementam várias interfaces comuns).
Por exemplo, se você tentar o seguinte:
Você notará que o tipo resultante da expressão condicional é
java.util.Date
uma vez que é a "Superclasse Menos Comum" do parTimestamp
/Time
.Como
null
pode ser autoboxed para qualquer coisa, a "Superclasse Menos Comum" é aInteger
classe e este será o tipo de retorno da expressão condicional (operador ternário) acima. O valor de retorno será um ponteiro nulo do tipoInteger
e é isso que será retornado pelo operador ternário.Em tempo de execução, quando a Máquina Virtual Java unboxes a
Integer
umNullPointerException
é lançada. Isso acontece porque a JVM tenta chamar a funçãonull.intValue()
, ondenull
é o resultado da caixa automática.Na minha opinião (e como minha opinião não está na Especificação de linguagem Java, muitas pessoas acharão errado de qualquer maneira), o compilador faz um mau trabalho ao avaliar a expressão em sua pergunta. Dado que você escreveu,
true ? param1 : param2
o compilador deve determinar imediatamente que o primeiro parâmetro -null
- será retornado e deverá gerar um erro no compilador. Isso é um pouco parecido com quando você escrevewhile(true){} etc...
e o compilador reclama do código abaixo do loop e o sinalizaUnreachable Statements
.Seu segundo caso é bem direto e essa resposta já é muito longa ...;)
CORREÇÃO:
Depois de outra análise, acredito que errei ao dizer que um
null
valor pode ser colocado em caixa / caixa automática para qualquer coisa. Falando sobre a classe Inteiro, o boxe explícito consiste em invocar onew Integer(...)
construtor ou talvez oInteger.valueOf(int i);
(encontrei esta versão em algum lugar). O primeiro jogaria umNumberFormatException
(e isso não acontece) enquanto o segundo simplesmente não faria sentido, pois umint
não pode sernull
...fonte
null
código original do OP não está em caixa. A maneira como funciona é: o compilador assume quenull
é uma referência a um número inteiro. Usando as regras para tipos de expressões ternárias, ele decide que a expressão inteira é uma expressão Inteira. Em seguida, ele gera código para autoboxar o1
(caso a condição seja avaliada comofalse
). Durante a execução, a condição é avaliada paratrue
que a expressão seja avaliadanull
. Ao tentar retornar umint
da função, onull
está fora da caixa. Isso lança um NPE. (O compilador pode otimizar a maioria desta distância.)Na verdade, no primeiro caso, a expressão pode ser avaliada, pois o compilador sabe que deve ser avaliada como uma
Integer
, no entanto, no segundo caso, o tipo do valor de retorno (null
) não pode ser determinado, portanto não pode ser compilado. Se você o converterInteger
, o código será compilado.fonte
fonte
Que tal agora:
A saída é verdadeira, verdadeira.
A cor do Eclipse codifica o 1 na expressão condicional como caixa automática.
Meu palpite é que o compilador está vendo o tipo de retorno da expressão como Object.
fonte