Как вызвать перегруженные методы Java в Clojure - PullRequest
15 голосов
/ 27 апреля 2010

Для этого примера класс Java:

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

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

Когда я запускаю Clojure и пытаюсь вызвать метод test (int), вместо этого вызывается метод test (Object), поскольку Clojure автоматически упаковывает целое число в объект java.lang.Integer.

Как заставить Clojure вызвать метод test (int)?

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

Я хочу вызывать методы типа Component.add(Component comp, int index) в AWT, но вместо этого продолжаю вызывать add(Component comp, Object constraints), поэтому кнопки на моей панели инструментов всегда отображаются в неправильном порядке.

Ответы [ 2 ]

15 голосов
/ 28 апреля 2010

Сейчас на канале #clojure на Freenode идет обсуждение этой темы. Крис Хаузер (который собирался опубликовать ответ, но в итоге решил, что слишком занят, чтобы сделать это) опубликовал Суть , которая демонстрирует, что происходит с перегруженным методом boolean против Object; Оказывается, что в некоторых сценариях, в дополнение к (boolean ...), требуется подсказка типа. Дискуссия была довольно поучительной: несколько темных углов процесса компиляции Clojure стали хорошо освещенными. (См. Ссылки на журнал IRC ниже.)

В принципе, если объект создается прямо в форме вызова метода - (.foo (Foo.) ...), скажем, - подсказка типа не нужна; это также не является необходимым, если объект был создан как значение для local в форме let (см. обновление 2 ниже и мою версию Gist). Если объект получен поиском Var, тем не менее, требуется подсказка типа - которая может быть предоставлена ​​либо на самом Var, либо на сайте вызова, на символе, используемом для ссылки на Var.

Java-код от Gist:

package mypkg;

public class Ugly {
    public Ugly(){}
    public String foo(boolean i) { return "bool: " + i; }
    public String foo(Object o) { return "obj: " + o; }
}

И код 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"

Обратите внимание, что компилятору Clojure требуется подсказка типа на u, чтобы иметь возможность компилировать прямой вызов метода. В противном случае кажется, что генерируется основанный на отражении код, который, очевидно, теряет тот факт, что аргумент должен быть примитивом на этом пути.

Мои дополнения следуют (и вот мой форк вышеупомянутого 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"

Тема была впервые поднята на данный момент . Chouser опубликовал Gist через полчаса , после чего обсуждение становится все более и более интересным.

8 голосов
/ 27 апреля 2010
user=> (.test (foo.TestInterop.) 10)
"Test(Object)"
user=> (.test (foo.TestInterop.) (int 10))
"Test(int)"

Числа в Clojure обычно заключены в квадрат (int => Integer), если только вы не запросите примитивы.

Здесь - дополнительная информация о примитивах в Clojure.

...