Как я могу добавить typehint в clojure, чтобы исправить предупреждение о том, что «ctor не может быть разрешен», т.е. Вызов конструктора?

#clojure #clojure-java-interop

#clojure #clojure-java-взаимодействие

Вопрос:

Следующий пример функции, которая использует специальную форму Clojure для взаимодействия с Java для вызова конструктора класса, вызывает предупреждение об отражении:

 (defn test-reflection-err []
  (new java.util.HashMap {}))
 

Это сообщение гласит:

 Reflection warning, /Users/ethan/Projects/scicloj/tablecloth.time/src/tablecloth/time/index.clj:26:3 - call to java.util.HashMap ctor can't be resolved.
 

Я пробовал размещать подсказки типа, чтобы избежать этого, но не уверен, где их разместить, чтобы предотвратить ошибку отражения. Кто-нибудь знает, как это сделать?

Я пробовал:

 (defn test-reflection-err []
  (^TreeMap new java.util.HashMap {}))
 

и

 (defn test-reflection-err []
  (doto ^TreeMap (new java.util.HashMap {})))
 

Ответ №1:

Вам нужно добавить подсказку к аргументу конструктора:

 (let [^java.util.Map m {}]
  (new java.util.HashMap m))
 

Комментарии:

1. Я делаю что-то не так? Я все еще получаю предупреждение об отражении с помощью этой техники. Это удается, с подсказкой типа или без нее.

2. @AlanThompson — я не уверен, что spyxx делает, но для меня в clojure 1.10.1 REPL, если я делаю (set! *warn-on-reflection* true) , тогда (new java.util.HashMap {}) появляется предупреждение об отражении, а (let [^java.util.Map m {}] (new java.util.HashMap m)) не нет. Я думаю, что ваш подсказка типа для m2 находится, по крайней мере, не в том месте, и так и должно быть (def ^java.util.Map m2 {})

3. Я добавил некоторые обновления к другому ответу, чтобы прояснить неожиданное поведение, которое я наблюдал.

4. @Lee интересно, можете ли вы объяснить, может быть, даже добавить к ответу, почему это работает. Одна вещь, которая меня смущает, это то, что предупреждение о отражении говорит, что это вызов ctor HashMap, который он не может разрешить. Тем не менее, мы исправляем это, добавляя подсказку типа к аргументу Map, который передается в ctor HashMap. Было бы здорово, если бы вы могли внести больше ясности в то, что происходит — если позволяет время, конечно ;).

Ответ №2:

Если вам действительно не нужно по какой-то причине исключить все отражения, самый простой ответ — поместить эту строку в свой project.clj :

 :global-vars {*warn-on-reflection* false}
 

Я не смог получить подсказку типа, чтобы устранить предупреждение при использовании пустой карты Clojure в качестве аргумента конструктора. Соблюдайте этот код:

 (ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))

(dotest
  (newline)
  (spyxx {:a 1}) ; `spyxx` prints expression, type, and value

  (newline)
  (spyxx (java.util.HashMap.))     ; Works
  (spyxx (new java.util.HashMap))  ; Works

  (newline)
  (let [m ^java.util.Map {}]
    (spyxx (new java.util.HashMap m))  ; Reflection Warning
    (spyxx (java.util.HashMap. m)))    ; Reflection Warning

  (newline)
  (let [^java.util.Map m {}]
    (spyxx (new java.util.HashMap m))  ; Works
    (spyxx (java.util.HashMap. m))))   ; Works

(def ^java.util.Map m3 {})
(dotest
  (newline)
  (spyxx m3)
  (spyxx (java.util.HashMap. m3))) ; Works
 

со следующими результатами. Обратите внимание, что все они работают, но вы получаете предупреждение об отражении, где указано выше:

 --------------------------------------
   Clojure 1.10.2-alpha1    Java 15
--------------------------------------

Testing tst.demo.core

{:a 1} => <#clojure.lang.PersistentArrayMap {:a 1}>

(java.util.HashMap.)     => <#java.util.HashMap {}>
(new java.util.HashMap)  => <#java.util.HashMap {}>

(new java.util.HashMap m)  => <#java.util.HashMap {}>
(java.util.HashMap. m)     => <#java.util.HashMap {}>

(new java.util.HashMap m)  => <#java.util.HashMap {}>
(java.util.HashMap. m)     => <#java.util.HashMap {}>

m3 => <#clojure.lang.PersistentArrayMap {}>
(java.util.HashMap. m3)   => <#java.util.HashMap {}>
 

Кажется, вам не нужна пустая карта Clojure в качестве аргумента конструктора, и тогда проблема исчезнет. Вы также можете убрать предупреждение, установив значение *warn-on-reflection* false (либо в project.clj коде, либо вручную в коде).

Если вы используете подсказку типа, ее легко получить в неправильном месте, где она будет молча проигнорирована.


Обновить

Рассмотрим этот дополнительный код:

 (dotest
  (newline)
  (set! *warn-on-reflection* false) ; not executed upon parsing
  (let [m4 ^java.util.Map {}]
    (spyxx (new java.util.HashMap m4)) ; Reflection Warning
    (spyxx (java.util.HashMap. m4))))  ; Reflection Warning

(newline)
; vvv executed when parsed before following lines
(set! *warn-on-reflection* false)
(let [m5 ^java.util.Map {}]
  (spyxx (new java.util.HashMap m5))  ; Works
  (spyxx (java.util.HashMap. m5)))    ; Works
 

Первый тест генерирует предупреждение об отражении при анализе (перед запуском теста), поскольку анализатор, похоже, оценивает константные выражения (предположение).

2-й тест не генерирует предупреждений, поскольку Clojure читает, анализирует и оценивает каждую форму по очереди. Таким образом, вызов (set! *warn-on-reflection* false)
для отключения предупреждений перед чтением и оценкой следующих строк.

Комментарии:

1. Все более ранние зрители, пожалуйста, просмотрите обновленный ответ на предмет исправлений и дополнительных деталей.