2010-04-27 24 views
15

Para este ejemplo de la clase de Java:métodos Como llamar sobrecargado Java en Clojure

package foo; 
public class TestInterop 
{ public String test(int i) 
    { return "Test(int)"; } 

    public String test(Object i) 
    { return "Test(Object)"; } 
} 

Cuando comienzo Clojure y trato de llamar al método de prueba (int), el método de ensayo (objeto) se llama en su lugar, porque Clojure coloca automáticamente el entero en un objeto java.lang.Integer.

¿Cómo forzo a Clojure a llamar al método de prueba (int)?

user=> (.test (new foo.TestInterop) 10) 
"Test(Object)" 

Quiero llamar a métodos como Component.add(Component comp, int index) en AWT, pero en lugar de seguir llamando add(Component comp, Object constraints), por lo que los botones de mi barra de herramientas siempre aparecen en el orden incorrecto.

Respuesta

14

Se está llevando a cabo un debate en el canal #clojure en Freenode sobre este mismo tema. Chris Houser (que iba a publicar una respuesta, pero finalmente decidió que estaba demasiado ocupado como para hacerlo) ha publicado a Gist que demuestra lo que sucede con un método sobrecargado boolean frente a Object; resulta que en algunos escenarios, además de un molde (boolean ...), se requiere una pista de tipo. La discusión fue bastante esclarecedora, con algunos rincones oscuros del proceso de compilación de Clojure iluminados. (Consulte los enlaces al registro de IRC a continuación.)

Básicamente, si se crea un objeto directamente en el formulario de llamada al método - (.foo (Foo.) ...), digamos que esa sugerencia de tipo no es necesaria; tampoco es necesario si el objeto se ha construido como un valor para un local en un formulario adjunto let (ver la actualización 2 a continuación y mi versión del Gist). Sin embargo, si el objeto se obtiene mediante la búsqueda Var, se requiere una sugerencia de tipo, que se puede proporcionar en el propio Var o, en el sitio de la llamada, en el símbolo utilizado para referirse al Var.

código

El Java desde el Gist:

Y el código Clojure:

(.foo (mypkg.Ugly.) 5) 
;=> "obj: 5" 

(.foo (mypkg.Ugly.) true) 
;=> "obj: true" 

(.foo (mypkg.Ugly.) (boolean true)) 
;=> "bool: true" 


(def u (mypkg.Ugly.)) 
(.foo u (boolean true)) 
;=> "obj: true" 

(.foo #^mypkg.Ugly u (boolean true)) 
;=> "bool: true" 

Nota cómo el compilador de Clojure necesita un toque tipo de u a ser capaz de compilar una llamada al método directo . De lo contrario, parece que se genera un código basado en la reflexión, que aparentemente pierde de vista el hecho de que el argumento debe ser primitivo en el camino.

Mis adiciones siguen (y aquí está my fork of the above Gist).

;; renamed mypkg.Ugly to foo.TestInterop2 when doing my tests 
user> (let [t (foo.TestInterop2.)] 
     (.foo t (boolean true))) 
"bool: true" 

;;; type-hinting the Var 
user> (def #^foo.TestInterop2 x (foo.TestInterop2.)) 
#'user/x 
user> (.foo x (boolean true)) 
"bool: true" 

El tema fue presentado por primera vez at this point. Chouser publicó el Gist half an hour later, y la discusión se volvió más y más interesante después de eso.

+0

Eso explica por qué siempre estuvo trabajando en los programas de prueba y no en el programa real. Puse la sugerencia de tipo sobre la llamada, y ahora está funcionando bien. Muchas gracias. Tuve la oportunidad de crear una macro para esto, ya que puede obtener la clase de la variable, pero parece que las sugerencias de tipo no macro-ise. –

+0

¡Feliz de escuchar eso! Ah, y en realidad es posible manejar sugerencias de tipo en macros, y algunas veces una macro bien diseñada puede producir un código bien insinuado con un mecanografía mínima adicional. Puede publicar una pregunta por separado si desea ayuda con la redacción de una. –

+0

No pude hacer que esto funcione con mi código, aunque mi situación es un poco diferente. Los métodos de Java que estoy tratando de llamar son los JavaFX 'Application.launch'. Deseo el método estático 'launch' con el argumento' Class 'y parece que no puede proporcionar una sugerencia de tipo para mi llamada (he intentado muchos enfoques) que hace que se llame al método correcto de 'lanzamiento' y obtengo los errores de 'La clase no se puede convertir en cadena'. – Jason

8
user=> (.test (foo.TestInterop.) 10) 
"Test(Object)" 
user=> (.test (foo.TestInterop.) (int 10)) 
"Test(int)" 

Los números en Clojure son generalmente en cajas (int => Entero) a menos que preguntar específicamente para primitivas.

Here es más información sobre las primitivas en Clojure.

+0

Eso es genial. Muchas gracias. –

Cuestiones relacionadas