2008-11-14 19 views
27

Durante la navegación de la clase Método me encontré con la función isBridge(), cuyo javadoc dice, que es verdadera solo si la especificación java declara el método como verdadero.¿para qué se utiliza java.lang.reflect.Method.isBridge()?

Por favor, ayúdenme a entender para qué se utiliza esto? ¿Puede una clase personalizada declarar su método como un puente si es necesario?

Respuesta

27

El compilador puede crear un método de puente al extender un tipo parametrizado cuyos métodos tienen argumentos parametrizados.

Puede encontrar en esta clase BridgeMethodResolver una forma de obtener el Método real referido por un 'método de puente'.

Ver Create Frame, Synchronize, Transfer Control:

Como ejemplo de tal situación, tenga en cuenta las declaraciones:

class C<T> { abstract T id(T x); } 
class D extends C<String> { String id(String x) { return x; } } 

Ahora, dada una invocación

C c = new D(); 
c.id(new Object()); // fails with a ClassCastException 

se invoca el borrado del método actual , D.id(String) difiere en su firma de la declaración del método de tiempo de compilación, C.id(Object). El primero toma un argumento de tipo String mientras que el segundo toma un argumento de tipo Object. La invocación falla con una ClassCastException antes de que se ejecute el cuerpo del método.

Tales situaciones solo pueden surgir si el programa da lugar a una advertencia no marcada (§5.1.9).

Las implementaciones pueden reforzar estas semánticas mediante la creación de métodos de puente. En el ejemplo anterior, el siguiente método puente se crearía en la clase D:

Object id(Object x) { return id((String) x); } 

Este es el método que realmente sería invocada por la máquina virtual de Java en respuesta a la llamada c.id(new Object()) se muestra arriba, y se ejecutará el yeso y el fracaso, según sea necesario.

Ver también Bridge:

como se menciona en el comentario, también se necesitan métodos de puente para covariante anulando:

  • En Java 1.4, y anteriores, un método puede anular otro si las firmas coinciden exactamente con .
  • En Java 5, un método puede anular otro si los argumentos coinciden exactamente pero el tipo de retorno del método de primer orden, si es un subtipo del tipo de retorno del otro método.

Típicamente, un método Object clone() puede ser anulado por un MyObject clone(), pero un método puente será generado por el compilador:

public bridge Object MyObject.clone(); 
+2

Incluso sin genéricos, los métodos de puente son necesarios para los tipos de retorno covariantes. –

+0

El enlace BridgeMethodResolver está roto :( – BrunoJCM

+0

@BrunoJCM good catch. He restaurado el enlace (la clase se ha movido del proyecto 'org.springframework.core' al proyecto' spring-framework': https://fisheye.springsource.org /browse/spring-framework/org.springframework.core/src/main/java/org/springframework/core/BridgeMethodResolver.java#rdc41daa3db350ef9a4b14ef1d750d79cb22cf431) – VonC

3

El ejemplo mostrado allí (cita de la JLS) hace que suene como los métodos de puente solo se usan en situaciones donde se usan tipos brutos.Como este no es el caso, pensé en utilizar un ejemplo en el que se usan métodos de puente para un código genérico totalmente correcto.

Considere el siguiente interfaz y la función:

public static interface Function<A,R> { 
    public R apply (A arg); 
} 
public static <A, R> R applyFunc (Function<A,R> func, A arg) { 
    return func.apply(arg); 
} 

Si utiliza el código de la siguiente manera, se utiliza un método de puente:

Function<String, String> lower = new Function<String, String>() { 
    public String apply (String arg) { 
     return arg.toLowerCase(); 
    } 
}; 
applyFunc(lower, "Hello"); 

Tras el borrado, la interfaz Function contiene el método apply(Object)Object (que puede confirmar descompilando el bytecode). Naturalmente, si nos fijamos en el código descompilado para applyFunc, verá que contiene una llamada al apply(Object)Object. Object es el límite superior de sus variables de tipo, por lo que ninguna otra firma tendría sentido.

Por lo tanto, cuando se crea una clase anónima con el método apply(String)String, en realidad no implementa la interfaz Function a menos que se cree un método puente. El método de puente permite que todo el código genérico ingresado haga uso de la implementación Function.

Curiosamente, sólo si la clase implementa alguna otra interfaz con la firma apply(String)String y sólo si el método se llama a través de una referencia de ese tipo de interfaz sería el compilador nunca emitir una llamada con esa firma.

Incluso si tengo el siguiente código:

Function<String, String> lower = ...; 
lower.apply("Hello"); 

El compilador todavía emite una llamada a apply(Object)Object.

En realidad, hay otra manera de conseguir el compilador para llamar apply(String)String, pero toma ventaja del tipo mágico asignado a una expresión de creación clase anónima que no pueden de otra manera ser escrito:

new Function<String, String>() { 
    public String apply (String arg) { 
     return arg.toLowerCase(); 
    } 
}.apply("Hello"); 
0

Otro caso me tropezó no tiene nada que ver con los genéricos:

protected abstract class Super { 
    public void m() {} 
} 
public class Sub extends Super {} 
assert Sub.class.getMethod("m").isBridge();