2010-04-27 4 views
15

: méthode, la méthode d'essai (Object)Comment faire pour cet exemple Java de classe I appel surchargées méthodes Java dans Clojure

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

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

Quand je commence Clojure et essaie d'appeler le test (int) est appelé à la place, car Clojure place automatiquement l'entier dans un objet java.lang.Integer.

Comment forcer Clojure à appeler la méthode test (int)?

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

Je veux appeler des méthodes comme Component.add(Component comp, int index) dans AWT, mais plutôt continuer à appeler add(Component comp, Object constraints), de sorte que les boutons de ma barre d'outils apparaissent toujours dans le mauvais ordre.

Répondre

14

Une discussion est en cours dans le canal #clojure sur Freenode à ce sujet. Chris Houser (qui allait poster une réponse, mais a finalement décidé qu'il était trop occupé pour le faire) a posté a Gist qui montre ce qui se passe avec une méthode surchargée boolean vs Object; il s'avère que dans certains scénarios, en plus d'un cast (boolean ...), un indice de type est requis. La discussion a été très instructive, avec quelques coins sombres du processus de compilation de Clojure devenant joliment éclairés. (Voir les liens vers le journal IRC ci-dessous.)

Fondamentalement, si un objet est créé directement dans le formulaire d'appel de méthode - (.foo (Foo.) ...), dites - ce type d'indice n'est pas nécessaire; il n'est pas non plus nécessaire si l'objet a été construit comme une valeur pour un local dans un formulaire let (voir la mise à jour 2 ci-dessous et ma version du Gist). Si l'objet est obtenu par la recherche Var, cependant, un indice de type est requis - qui peut être fourni soit sur le Var lui-même ou, sur le site d'appel, sur le symbole utilisé pour faire référence à la Var.

Le code Java à partir du Gist:

Et le code 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" 

Notez comment le compilateur Clojure a besoin d'un indice de type sur u pour être en mesure de compiler un appel de méthode directe . Sinon, le code basé sur la réflexion semble être généré, ce qui semble perdre de vue le fait que l'argument est censé être primitif en cours de route. Mes ajouts suivent (et voici 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" 

Le sujet a été abordé at this point. Chouser posté le Gist half an hour later, avec la discussion devient de plus en plus intéressant après cela.

+0

Cela explique pourquoi il fonctionnait toujours dans les programmes de test et non dans le vrai programme. J'ai mis l'indice de type sur l'appel, et ça fonctionne maintenant très bien. Merci beaucoup. J'ai eu une chance de créer une macro pour cela, puisque vous pouvez obtenir la classe à partir de la variable, mais il semble que les indications de type ne soient pas macro-isées. –

+0

Heureux d'entendre ça! Oh, et en fait, il est possible de gérer les indications de type dans les macros - et parfois une macro bien conçue peut produire du code joliment suggéré avec un minimum de frappe supplémentaire. Vous pourriez poser une question distincte à ce sujet si vous voulez de l'aide pour en écrire un. –

+0

Je n'ai pas réussi à faire fonctionner ça avec mon code, même si ma situation est un peu différente. Les méthodes Java que j'essaie d'appeler sont JavaFX 'Application.launch'. Je souhaite la méthode statique 'launch' avec l'argument' Class 'et ne peut pas donner un indice de type à mon appel (j'ai essayé plusieurs approches) qui provoque l'appel de la méthode' launch' correcte et j'obtiens des erreurs "La classe ne peut pas être convertie en chaîne". – Jason

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

Les nombres dans Clojure sont généralement encadrés (int => entier) sauf si vous demandez spécifiquement des primitives.

Here est plus d'informations sur les primitives dans Clojure.

+0

C'est génial. Merci beaucoup. –

Questions connexes