defrecord 构造函数中未强制执行类型提示

Posted

技术标签:

【中文标题】defrecord 构造函数中未强制执行类型提示【英文标题】:Type hinting not enforced in defrecord constructors 【发布时间】:2011-03-21 16:00:55 【问题描述】:

我使用带有字段类型提示的defrecord 创建了一个类型。然而,我发现这些类型提示并没有在构造函数中强制执行,我可以用它们做一些奇怪的事情。以下面的 sn-p 为例:

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"

显示的构造函数签名与提供的类型提示不匹配(它们对StringInteger 都使用Object)并且我能够构造具有错误字段类型的对象。

我的代码有问题还是 Clojure 中的错误?

我正在使用 Clojure 1.2.0-beta1。

【问题讨论】:

顺便说一句,如果你的代码依赖于 1.2 的特性,你应该更喜欢 ^ 而不是 #^ 来引入阅读器元数据; #^ 的旧含义在 1.2 中已弃用。 【参考方案1】:

类型提示用于避免反射;它们不(当前)用于静态类型函数或构造函数参数(例外是原语,因为它们不能包含在Object 下)。因此,它们对简单记录的作用不大,但在添加协议实现时它们确实很重要,例如:

用户=>(设置!*warn-on-reflection* true) 真的 用户=> (defprotocol P (foo [p])) 磷 user=> (defrecord R [s] P (foo [_] (.getBytes s))) ; getBytes 是 String 上的一个方法 反射警告,NO_SOURCE_PATH:6 - 无法解析对字段 getBytes 的引用。 用户.R 用户=> (foo (R. 5)) java.lang.IllegalArgumentException:未找到匹配字段:类 java.lang.Integer 的 getBytes (NO_SOURCE_FILE:0) 用户=> (defrecord R [^String s] P (foo [_] (.getBytes s))) 用户.R 用户=> (foo (R. 5)) java.lang.ClassCastException: java.lang.Integer 不能转换为 java.lang.String (NO_SOURCE_FILE:0)

两个版本的区别在于后者发出调用String.getBytecode()的字节码(因此在传递一个整数时会出现ClassCastException),而前者需要查找.getBytes相对于传递给的运行时对象的确切含义函数(传递整数时该过程失败)。

【讨论】:

【参考方案2】:

据我所知,deftypedefprotocol 字段上的类型提示目前仅在涉及原始类型时才强制执行:

(deftype Foo [^int x])

(Foo. 5)    ; => OK
(Foo. :foo) ; => no go

;; ... and likewise with defprotocol

我非常模糊地记得这是一个公认的问题,但我不确定计划是记录这种行为还是强制执行非原始提示......我会试着找出答案。

【讨论】:

以上是关于defrecord 构造函数中未强制执行类型提示的主要内容,如果未能解决你的问题,请参考以下文章

dotnet C# 如果在构造函数抛出异常 析构函数是否会执行

C++ 构造函数 & 析构函数

如何强制调用类的全局实例的析构函数和构造函数(因此“重新初始化”类实例)

如何强制编译器显示隐式构造函数

构造函数与析构函数

为什么构造函数不能声明为虚函数,析构函数可以