Java Generics – método de ponte?

Algo chamado conceito de “método de ponte” relacionado a Java Generics me fez parar em um ponto e pensar sobre isso.

Btw, eu só sei que isso ocorre no nível de bytecode e não está disponível para nós usarmos.

Mas estou ansioso para conhecer o conceito por trás do “método bridge” usado pelo compilador Java.

O que exatamente acontece nos bastidores e por que é usado?

Qualquer ajuda com um exemplo seria muito apreciada.

É um método que permite que uma class estendendo uma class genérica ou implementando uma interface genérica (com um parâmetro de tipo concreto) ainda seja usada como um tipo bruto.

Imagina isto:

public class MyComparator implements Comparator { public int compare(Integer a, Integer b) { // } } 

Isso não pode ser usado em sua forma bruta, passando dois Object s para comparar, porque os tipos são compilados no método de comparação (ao contrário do que aconteceria se fosse um parâmetro de tipo genérico T, onde o tipo seria apagado). Então, em vez disso, nos bastidores, o compilador adiciona um “método de ponte”, que é algo como isto (se fosse fonte Java):

 public class MyComparator implements Comparator { public int compare(Integer a, Integer b) { // } //THIS is a "bridge method" public int compare(Object a, Object b) { return compare((Integer)a, (Integer)b); } } 

O compilador protege o access ao método bridge, impondo que chamadas explícitas diretamente resultem em um erro de tempo de compilation. Agora a class também pode ser usada em sua forma bruta:

 Object a = 5; Object b = 6; Comparator rawComp = new MyComparator(); int comp = rawComp.compare(a, b); 

Por que mais isso é necessário?

Além de adicionar suporte para uso explícito de tipos brutos (que é principalmente para compatibilidade com versões anteriores), os methods de ponte também são necessários para suportar o apagamento de tipos. Com o tipo de apagamento, um método como este:

 public  T max(List list, Comparator comp) { T biggestSoFar = list.get(0); for ( T t : list ) { if (comp.compare(t, biggestSoFar) > 0) { biggestSoFar = t; } } return biggestSoFar; } 

Na verdade, é compilado em bytecode compatível com isso:

 public Object max(List list, Comparator comp) { Object biggestSoFar = list.get(0); for ( Object t : list ) { if (comp.compare(t, biggestSoFar) > 0) { //IMPORTANT biggestSoFar = t; } } return biggestSoFar; } 

Se o método da ponte não existisse e você passasse um List e um MyComparator a esta function, a chamada na linha etiquetada IMPORTANT falharia desde que MyComparator não teria nenhum método chamado compare que toma dois Object s … somente um que leva dois Integer s.

O FAQ abaixo é uma boa leitura.

Veja também:

  • The Generics FAQ – O que é um método de ponte?
  • Métodos de ponte Java explicados (obrigado @Bozho )

É interessante notar que o compilador infere o método do MyComparator :

 public int compare(Integer a, Integer b) {/* code */} 

está tentando sobrescrever o Comparator

 public int compare(T a, T b); 

do tipo declarado Comparator . Caso contrário, a MyComparator do MyComparator seria tratada pelo compilador como um método adicional (sobrecarga) e não de sobreposição. E como tal, não teria nenhum método de ponte criado para isso.

Se você quiser entender por que você precisa de um método de ponte, é melhor entender o que acontece sem ele. Suponha que não exista um método de ponte.

 class A{ private T value; public void set(T newVal){ value=newVal } } class B extends A{ public void set(String newVal){ System.out.println(newVal); super.set(newVal); } } 

Observe que, após o apagamento, o método set em A tornou-se public void set(Object newVal) já que não há limite no parâmetro Type T. Não há nenhum método na class B cuja assinatura é a mesma que está set em A. Portanto, não há sobrepor. Assim, quando algo assim aconteceu:

 A a=new B(); a.set("Hello World!"); 

O polymorphism não funcionará aqui. Lembre-se de que você precisa sobrescrever o método da class pai na class filho para que você possa usar a class pai var para acionar o polymorphism.

O método de ponte é silenciosamente replace o método na class pai com todas as informações de um método com o mesmo nome, mas com assinatura diferente. Com a ajuda do método de ponte, o polymorphism funcionou. Embora na superfície, você substitui o método da class pai por um método de assinatura diferente.