好吧,我的identical?
心智模型不准确。它调用clojure.lang.Util/identical
,其实现如下
static public boolean identical(Object k1, Object k2){
return k1 == k2;
}
当Clojure用这种签名调用函数时,它必须将参数中的原始值装箱。double通过调用Double.valueOf(double)
装箱。NaN的特殊等价语义使得示例令人困惑,但在每一种情况下,identical?
都比较装箱值的引用等价性,而不是数值等价性。
通过查看反汇编的字节码(见下文),我们可以看到发生了什么。这里是总结。
- 在示例1、2和4中,每个传递给
identical?
的参数都单独装箱。
- 在示例3中,
##NaN
仅装箱一次,相同的装箱值用于identical?
的两个参数。
这里有更多示例。注意,##Inf
的行为与##NaN
的行为一致。
(identical? ##Inf ##Inf) ; true
(let [x ##Inf] (identical? x x)) ; false
(identical? 1.0 1.0) ; false
(let [x 1.0] (identical? x x)) ; false
(def my-one 1.0)
(identical? my-one my-one) ; true
结论
一切如预期工作。我对 identical?
的理解是错误的。它更像 (Object)... == (Object) ...
而非简单的 ==
。
经验教训:只使用 identical?
检查引用相等。
反汇编示例
代码已被使用 clj-java-decompiler 反汇编。
示例 1 (identical? Double/NaN Double/NaN)
class user$fn_line_1__254
Minor version: 0
Major version: 52
Flags: PUBLIC, FINAL, SUPER
public void <init>();
Flags: PUBLIC
Code:
linenumber 1
0: aload_0
1: invokespecial clojure/lang/AFunction.<init>:()V
4: return
public static java.lang.Object invokeStatic();
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: getstatic java/lang/Double.NaN:D
3: invokestatic java/lang/Double.valueOf:(D)Ljava/lang/Double;
linenumber 1
6: getstatic java/lang/Double.NaN:D
9: invokestatic java/lang/Double.valueOf:(D)Ljava/lang/Double;
linenumber 1
12: invokestatic clojure/lang/Util.identical:(Ljava/lang/Object;Ljava/lang/Object;)Z
15: ifeq 24
18: getstatic java/lang/Boolean.TRUE:Ljava/lang/Boolean;
21: goto 27
24: getstatic java/lang/Boolean.FALSE:Ljava/lang/Boolean;
27: areturn
StackMapTable: 00 02 18 42 07 00 1D
public java.lang.Object invoke();
Flags: PUBLIC
Code:
linenumber 1
0: invokestatic user$fn_line_1__254.invokeStatic:()Ljava/lang/Object;
3: areturn
static {};
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: return
示例 2 (let [x Double/NaN] (identical? x x))
省略;与示例 4 类似。
示例 3 (identical? ##NaN ##NaN)
注意装箱操作发生在静态块中。
class user$fn_line_1__246
Minor version: 0
Major version: 52
Flags: PUBLIC, FINAL, SUPER
public static final java.lang.Object const__1;
Flags: PUBLIC, STATIC, FINAL
public void <init>();
Flags: PUBLIC
Code:
linenumber 1
0: aload_0
1: invokespecial clojure/lang/AFunction.<init>:()V
4: return
public static java.lang.Object invokeStatic();
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: getstatic user$fn_line_1__246.const__1:Ljava/lang/Object;
3: getstatic user$fn_line_1__246.const__1:Ljava/lang/Object;
linenumber 1
6: invokestatic clojure/lang/Util.identical:(Ljava/lang/Object;Ljava/lang/Object;)Z
9: ifeq 18
12: getstatic java/lang/Boolean.TRUE:Ljava/lang/Boolean;
15: goto 21
18: getstatic java/lang/Boolean.FALSE:Ljava/lang/Boolean;
21: areturn
StackMapTable: 00 02 12 42 07 00 17
public java.lang.Object invoke();
Flags: PUBLIC
Code:
linenumber 1
0: invokestatic user$fn_line_1__246.invokeStatic:()Ljava/lang/Object;
3: areturn
static {};
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: ldc2_w NaN
3: invokestatic java/lang/Double.valueOf:(D)Ljava/lang/Double;
6: putstatic user$fn_line_1__246.const__1:Ljava/lang/Object;
9: return
示例 4 (let [x ##NaN] (identical? x x))
注意 invokeStatic 方法中有两个 Double.valueOf
调用。
class user$fn_line_1__250
Minor version: 0
Major version: 52
Flags: PUBLIC, FINAL, SUPER
public void <init>();
Flags: PUBLIC
Code:
linenumber 1
0: aload_0
1: invokespecial clojure/lang/AFunction.<init>:()V
4: return
public static java.lang.Object invokeStatic();
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: ldc2_w NaN
3: dstore_0 /* x */
4: dload_0 /* x */
5: invokestatic java/lang/Double.valueOf:(D)Ljava/lang/Double;
8: dload_0 /* x */
9: invokestatic java/lang/Double.valueOf:(D)Ljava/lang/Double;
linenumber 1
12: invokestatic clojure/lang/Util.identical:(Ljava/lang/Object;Ljava/lang/Object;)Z
15: ifeq 24
18: getstatic java/lang/Boolean.TRUE:Ljava/lang/Boolean;
21: goto 27
24: getstatic java/lang/Boolean.FALSE:Ljava/lang/Boolean;
27: areturn
StackMapTable: 00 02 FC 00 18 03 42 07 00 1B
public java.lang.Object invoke();
Flags: PUBLIC
Code:
linenumber 1
0: invokestatic user$fn_line_1__250.invokeStatic:()Ljava/lang/Object;
3: areturn
static {};
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: return