Я создал использование типа defrecord
с типом подсказывает для полей. Однако я нашел, что эти подсказки типа не осуществляются в конструкторах, и я могу сделать некоторые странные вещи с ними. Посмотрите на отрывок ниже, например:
user=> (defrecord Person [#^String name #^Integer age])
user.Person
user=> (seq (.getConstructors Person))
(#<Constructor public user.Person(java.lang.Object,java.lang.Object,
java.lang.Object,java.lang.Object)>
#<Constructor public user.Person(java.lang.Object,java.lang.Object)>)
user=> (Person. (Integer. 123) "abhinav")
#:user.Person{:name 123, :age "abhinav"}
Показанные подписи конструктора не соответствуют обеспеченным подсказкам типа (они используют Object
для обоих String
и Integer
) и я могу создать объекты с неправильными типами поля.
Есть ли что-то не так с моим кодом, или действительно ли это - ошибка в Clojure?
Я нахожусь на 1.2.0-beta1 Clojure.
Подсказки типов используются для того, чтобы избежать отражения; они не используются (в настоящее время) для статической типизации аргументов функций или конструкторов (исключением являются примитивы, поскольку они не могут быть подчинены Object
). Как таковые, они мало что дают для простой записи, но имеют значение, когда дело доходит до добавления реализации протокола, например:
user=> (set! *warn-on-reflection* true) true user=> (defprotocol P (foo [p])) P user=> (defrecord R [s] P (foo [_] (.getBytes s))) ; getBytes is a method on String Reflection warning, NO_SOURCE_PATH:6 - reference to field getBytes can't be resolved. user.R user=> (foo (R. 5)) java.lang.IllegalArgumentException: No matching field found: getBytes for class java.lang.Integer (NO_SOURCE_FILE:0) user=> (defrecord R [^String s] P (foo [_] (.getBytes s))) user.R user=> (foo (R. 5)) java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String (NO_SOURCE_FILE:0)
Разница между двумя версиями в том, что последняя выдает байткод, вызывая String. getBytecode()
(отсюда ClassCastException при передаче Integer), в то время как в первом случае нужно посмотреть, что именно .getBytes
означает в отношении объекта времени выполнения, переданного в функцию (и этот процесс не удается при передаче Integer).
Насколько я могу судить, подсказки типа в полях deftype
и defprotocol
в настоящее время применяются только тогда, когда задействован примитивный тип:
(deftype Foo [^int x])
(Foo. 5) ; => OK
(Foo. :foo) ; => no go
;; ... and likewise with defprotocol
Я очень смутно помню, что это общепризнанная проблема, хотя я не уверен, планируется ли задокументировать такое поведение или применить непримитивные подсказки ... Я постараюсь выяснить.