Как вызвать перегруженные Java-методы в Clojure

В этом примере класс 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), поэтому кнопки на моей панели инструментов всегда отображаются в неправильном порядке.

Ответы

Ответ 1

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

В принципе, если объект создается прямо в форме вызова метода - (.foo (Foo.) ...), скажем - этот тип подсказки не требуется; также не требуется, если объект был сконструирован как значение для локального в прилагаемой форме 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 для компиляции прямого вызова метода. В противном случае создается код, основанный на отражении, который, по-видимому, проигнорирует тот факт, что аргумент должен быть примитивным на этом пути.

Мои дополнения следуют (и здесь моя вилка вышеупомянутого Гиста).

;; 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 , и обсуждение стало более интересным после этого.

Ответ 2

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

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

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