Pergunta

Notei hoje que auto-boxing às vezes pode causar ambigüidade na resolução de sobrecarga método. O mais simples exemplo parece ser esta:

public class Test {
    static void f(Object a, boolean b) {}
    static void f(Object a, Object b) {}

    static void m(int a, boolean b) { f(a,b); }
}

Quando compilado, ele faz o seguinte erro:

Test.java:5: reference to f is ambiguous, both method
    f(java.lang.Object,boolean) in Test and method
    f(java.lang.Object,java.lang.Object) in Test match

static void m(int a, boolean b) { f(a, b); }
                                  ^

A correção para esse erro é trivial: basta usar explícita auto-boxing:

static void m(int a, boolean b) { f((Object)a, b); }

O que chama corretamente na primeira sobrecarga como esperado.

Então, por que a resolução de sobrecarga falhar? Por que não o compilador auto-box o primeiro argumento, e aceitar o segundo argumento normalmente? Por que eu tive com o pedido de auto-boxing explicitamente?

Foi útil?

Solução

Quando você lança o primeiro argumento para Object mesmo, o compilador irá coincidir com o método sem usar autoboxing (JLS3 15.12.2):

a primeira fase (§15.12.2.2) executa resolução de sobrecarga sem permitir boxe ou conversão unboxing, ou o utilização do método aridade variável invocação. Se nenhum método aplicável é encontrado durante esta fase, em seguida o processamento continua para o segundo fase.

Se você não lançá-lo explicitamente, ele irá para a segunda fase de tentar encontrar um método de correspondência, permitindo autoboxing, e em seguida, ele é de fato ambígua, porque o seu segundo argumento pode ser igualada por boolean ou objeto.

A segunda fase (§15.12.2.3) executa sobrecarregar resolução, permitindo boxing e unboxing, mas ainda impede a utilização de aridade variável invocação de método.

Por que, na segunda fase, não o compilador escolher o segundo método porque nenhum autoboxing do argumento boolean é necessário? Porque depois de ter encontrado os dois métodos de correspondência, apenas a conversão subtipo é usado para determinar o método mais específico dos dois, independentemente de qualquer boxe ou unboxing que teve lugar para combiná-los em primeiro lugar (§15.12.2.5).

Além disso: o compilador não pode sempre escolher o método específico mais com base no número de auto (un) boxe necessário. Ele ainda pode resultar em casos ambíguos. Por exemplo, este ainda é ambígua:

public class Test {
    static void f(Object a, boolean b) {}
    static void f(int a, Object b) {}

    static void m(int a, boolean b) { f(a, b); } // ambiguous
}

Lembre-se que o algoritmo para a escolha de um método de correspondência (em tempo de compilação passo 2) é fixo e descrito nas JLS. Uma vez que na fase 2 não existe autoboxing selectiva ou unboxing. O compilador irá localizar todas os métodos que são acessíveis (ambos os métodos nestes casos) e aplicável (novamente os dois métodos), e só então escolhe a mais um específico sem olhar para o boxe / unboxing, que é ambígua aqui.

Outras dicas

O compilador se auto-box o primeiro argumento. Uma vez que foi feito, é o segundo argumento que é ambígua, como poderia ser visto como quer boolean ou objeto.

Esta página explica as regras para autoboxing e escolher qual método invocar. O compilador primeiras tentativas para selecionar um método sem usar qualquer autoboxing em todos os , porque o boxe e desempenho carry unboxing penalidades. Se nenhum método pode ser selecionado sem recorrer ao boxe, como neste caso, em seguida, o boxe é sobre a mesa para todas argumentos para esse método.

Quando você diz que f (a, b ), o compilador está confuso sobre qual função que deve fazer referência a.

Isto porque a é um int , mas o argumento esperado em f é um objeto. Assim, o compliler decide converter a para um objeto. Agora o problema é que, se a pode ser convertido para um objeto, de modo que pode ser b .

Isto significa que a chamada de função pode fazer referência a qualquer definições. Isso faz com que a chamada ambígua.

Quando você converte a para um objeto manualmente, o compilador só olha para a correspondência mais próxima e, em seguida, se refere a ele.

Por que não o compilador selecionar o função que pode ser alcançado por "fazer o menor número possível de boxing / unboxing conversões "?

Veja o caso seguinte:

f(boolean a, Object b)
f(Object a , boolean b)

Se nós chamamos como f (boolean um, boolean b) , cuja função deveria escolher? É certo ambíguo? Da mesma forma, isso vai se tornar mais complexo quando um monte de argumentos estão presentes. Portanto, o compilador escolheu para lhe dar um aviso em seu lugar.

Uma vez que não há nenhuma maneira de saber que uma das funções do programador realmente destinados a chamada, o compilador dá um erro.

Então, por que a resolução de sobrecarga falhou? Por que não fez auto-box o compilador o primeiro argumento, e aceitar o O segundo argumento normalmente? Por que eu tem que pedido auto-boxing explicitamente?

Ele não aceitou o segundo argumento normalmente. Lembre-se que "boolean" pode ser encaixotado em um objeto também. Você poderia ter converter explicitamente o argumento booleano para Objeto bem e teria funcionado.

Consulte http: //java.sun. com / docs / books / jls / third_edition / html / expressions.html # 20448

O elenco ajuda porque então não boxe é necessário para encontrar o método de chamada. Sem o elenco da segunda tentativa é permitir que o boxe e, em seguida, também o booleano pode ser encaixotado.

É melhor ter especificações claras e compreensíveis para dizer o que vai acontecer do que para fazer as pessoas adivinhar.

O Java resolve compilador sobrecarregado métodos e construtores em fases. Na primeira fase [§15.12.2.2], identifica métodos aplicáveis ??por subtipar [§4.10]. Neste exemplo, nenhum método é aplicável, porque int não é um subtipo de objeto.

Na segunda fase [§15.12.2.3], o compilador identifica métodos aplicáveis ??por conversão método invocação [§5.3], que é uma combinação de autoboxing e subtipagem. O argumento int pode ser convertido para um número inteiro, o qual é um subtipo do objecto, para ambas as sobrecargas. O argumento booleano não precisa de conversão para o primeiro sobrecarga, e pode ser convertido para booleano, um subtipo do objecto, para a segunda. Portanto, ambos os métodos são aplicáveis ??na segunda fase.

Uma vez que mais de um método é aplicável, o compilador deve determinar o que é mais específico [§15.12.2.5]. Ele compara os tipos de parâmetro, e não os tipos de argumentos, e não Autobox eles. Objeto e boolean são tipos não relacionados, por isso eles são considerados igualmente específicas. Nenhum método é mais específico do que o outro, então a chamada de método é ambíguo.

Uma maneira de resolver a ambiguidade seria para alterar o parâmetro boolean digitar booleana, que é um subtipo de objeto. A primeira sobrecarga seria sempre mais específico (quando aplicável) que o segundo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top