我使用创建了一个类型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
and Integer
)并且我能够构造具有错误字段类型的对象。
我的代码有问题还是 Clojure 中的错误?
我使用的是 Clojure 1.2.0-beta1。
类型提示用于避免反射;它们(当前)不用于静态类型函数或构造函数参数(基元除外,因为它们不能包含在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()
(因此当传递一个 Integer 时会出现 ClassCastException),而前者需要查找到底是什么.getBytes
意味着关于传递给函数的运行时对象(并且当传递整数时该过程失败)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)