Sobrecarga com Autoboxing Ampliação e Var-arg


Quando uma classe possui métodos sobrecarregados o trabalho do compilador é decidir qual método chamar quando for invocado.
Por exemplo, passar um argumento byte para um método que recebe um int não tem problema, mas se existir uma sobrecarga com um argumento byte o compilador dará preferência a esse último porque não terá que fazer ampliação.

Sobrecarga com Boxing e Var-arg

No caso de ter dois métodos sobrecarregados, um com argumento long, por exemplo, e outro com argumento Integer, se for passado um parâmetro do tipo int o compilador escolherá o método com o argumento long, pois é menos custoso fazer cast de int pra long do que fazer autoboxing. Além disso, o compilador sempre optará pela forma mais antiga, e nesse caso o autoboxing é apenas a partir do Java 5.
No caso de ter dois métodos sobrecarregados, um com dois argumentos int e outro com um var-arg de byte, ao chamar o método passando duas variáveis byte, o método invocado será o com dois argumentos int, pois, apesar de requerer cast, o compilador irá escolher o modo mais antigo, já que var-arg é algo mais novo, deixando o código mais robusto.
Então, o cast (ampliação) de primitivos sempre terá preferência em relação ao autoboxing e var-arg, enquanto que o autoboxing sempre terá preferência em relação a var-arg, ou seja, o var-arg sempre será a última opção.


public class BoxAmpliacaoVararg {

      static void go(Integer x) {
            System.out.println("Integer");
      }

      static void go(long x) {
            System.out.println("long");
      }
     
      static void go(int x, int y) {
            System.out.println("int, int");
      }

      static void go(byte... x) {
            System.out.println("byte... ");
      }
     
      static void go(Byte x, Byte y) {
            System.out.println("Byte, Byte");
      }

      public static void main(String[] args) {
            /* é menos custoso fazer cast de int para
             * long do que fazer autoboxing de int
             * para Integer */
            int i = 5;
            go(i); // imprime long

            /* é menos custoso fazer cast de byte para
             * int do que utilizar var-arg de byte */
            byte b = 5;
            go(b, b); // imprime int, int
           
            /* o compilador dará mais pereferência
             * ao autoboxing de byte para Byte do que
             * utilizar o var-arg de byte */
            byte b2 = 5;
            go(b2, b2); // imprime Byte, Byte
      }

}


Sobrecarga combinando Ampliação e Boxing

No caso em que é requerida uma ampliação e auboxing juntos o compilador apresentará falha. Por exemplo, se houver um método com um parâmetro Long, e for passado um argumento byte, seria necessário primeiro fazer a ampliação de byte para long, para que depois o compilador pudesse fazer o autoboxing de long para Long. O compilador não fará isso automaticamente.


public class AmpliacaoBox {

      static void go(Long x) {
            System.out.println("Long");
      }

      public static void main(String[] args) {
            byte b = 5;
            /* erro - o compilador não aceita
             * fazer ampliação de byte para long
             * e ao mesmo tempo boxing de long
             * para Long */
            go(b);
           
            long l = b;
            go(l); //imprime Long
      }

}


Porém, se, nesse caso, o argumento for um Object, funcionará, pois o compilador irá primeiro fazer o autoboxing de byte para Byte, e o método poderá receber esse parâmetro, pois Byte IS-A Object.

public class BoxAmpliacao {

      static void go(Object o) {
            /* se o obj passado for Byte não
             * haverá problemas */
            Byte b2 = (Byte) o;
            System.out.println(b2);
      }

      public static void main(String[] args) {
            byte b = 5;
            go(b); //imprime 5
      }
     
}


Sobrecarga em Combinação com Var-arg

É possível combinar var-arg com ampliação ou autoboxing. Por exemplo, um método com um argumento var-arg do tipo long, pode ser invocado passando dois parâmetros int, pois o compilador irá ampliar para um long, e depois usará o var-arg. Um método com um argumento var-arg do tipo Integer também pode ser invocado passando dois parâmetros int, pois o compilador irá realizar o boxing de int para Integer, e depois usará o var-arg.


public class Sobrecarga_Vararg {

      static void wide_vararg(long... x) {
            System.out.println("long...");
      }

      static void box_vararg(Integer... x) {
            System.out.println("Integer...");
      }

      public static void main(String[] args) {
            int i = 5;
            /* o compilador irá fazer a ampliação
             * de int para long e depois utilizará
             * o vararg de long */
            wide_vararg(i, i); // imprime long
            /* o compilador irá fazer o autoboxing
             * de int para Integer e irá utilizar
             * o vararg de Integer */
            box_vararg(i, i); // imprime Integer
      }

}


Regras: a ampliação de primitivos usa o menor argumento de método possível; usado individualmente, boxing e var-args são compatíveis com overloading; não é permitido ampliar de uma classe wrapper para outra; não é permitido ampliar e depois fazer boxing (um int não pode se tornar um Long); é possível fazer boxing e ampliação (um int pode se tornar um Object, via Integer); é possível combinar var-args com ampliação e boxing.



Fonte: SCJP Sun Certified Programmer for Java 6 Study Guide

2 comentários:

Anônimo disse...

Ótimo texto!
Parabéns!

Anônimo disse...

Mto boa explicação!

Postar um comentário