代码之家  ›  专栏  ›  技术社区  ›  Abhinav Sarkar

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

  •  8
  • Abhinav Sarkar  · 技术社区  · 15 年前

    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中的一个bug?

    2 回复  |  直到 15 年前
        1
  •  9
  •   Alex Taggart    15 年前

    类型提示用于避免反射;它们(当前)不用于静态类型化函数或构造函数参数(异常是原语,因为它们不能包含在 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),而前者需要查找 .getBytes 对于传递给函数的运行时对象(当传递一个整数时,该进程将失败)。

        2
  •  6
  •   Michał Marczyk    15 年前

    据我所知,输入提示 deftype defprotocol 当前仅当涉及基元类型时才强制执行字段:

    (deftype Foo [^int x])
    
    (Foo. 5)    ; => OK
    (Foo. :foo) ; => no go
    
    ;; ... and likewise with defprotocol
    

    推荐文章