Подсказка типов не применяется в конструкторах defrecord
Я создал тип, используя 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?
Я на Clojure 1.2.0-бета1.
2 ответа
Тип-подсказки используются, чтобы избежать отражения; они (в настоящее время) не используются для статического ввода аргументов функций или конструкторов (за исключением примитивов, поскольку их нельзя включить в Object
). Как таковые, они не делают много для простой записи, но имеют значение, когда дело доходит до добавления реализации протокола, например:
пользователь => (установить! * предупреждение-на-отражение * true) правда user=> (defprotocol P (foo [p])) п user => (отменить запись R [s] P (foo [_] (.getBytes s))); getBytes - это метод для String Предупреждение об отражении, NO_SOURCE_PATH:6 - ссылка на поле getBytes не может быть разрешена. user.R user=> (foo (R. 5)) java.lang.IllegalArgumentException: не найдено подходящего поля: getBytes для класса java.lang.Integer (NO_SOURCE_FILE:0) user=> (отменить запись R [^String s] P (foo [_] (.getBytes s))) user.R user=> (foo (R. 5)) java.lang.ClassCastException: java.lang.Integer не может быть приведен к java.lang.String (NO_SOURCE_FILE:0)
Разница между двумя версиями заключается в том, что последняя испускает вызов байт-кода String.getBytecode()
(следовательно, ClassCastException при передаче целого числа), тогда как первый должен искать, что именно .getBytes
означает в отношении объекта времени выполнения, переданного в функцию (и этот процесс завершается неудачно, когда передается целое число).
Насколько я могу судить, тип подсказки на deftype
а также defprotocol
поля в настоящее время применяются только в том случае, если задействован примитивный тип:
(deftype Foo [^int x])
(Foo. 5) ; => OK
(Foo. :foo) ; => no go
;; ... and likewise with defprotocol
У меня есть очень смутное воспоминание о том, что это признанная проблема, хотя я не уверен, что планируется документировать это поведение или применять не примитивные подсказки... Я постараюсь выяснить.