我在对话框上挂了一个WindowListener并得到了这个错误
$ clj windowlistener.clj
WARNING: When invoking clojure.main, use -M
Syntax error (IllegalArgumentException) compiling reify* at (windowlistener.clj:9:10).
Can't define method not in interfaces: windowIconified
以下是代码。你能看出问题吗?
(import 'javax.swing.JOptionPane
'java.awt.event.WindowListener)
(let [jdialog (.createDialog
(JOptionPane. (System/getProperty "java.version"))
"WindowListener")
wl (reify WindowListener
(windowActivated [this e])
(windowClosed [this e])
(windowClosing [this e])
(windowDeactivated [this e])
(windowDeiconified [this e])
(windowIconified [this e])
(windowOpened [this e]))]
(.addWindowListener jdialog wl)
(.show jdialog))
我知道方法名拼写正确,因为我从Firefox的Javadoc显示中粘贴了它,然后重新排列了括号。哈哈,复制粘贴是我的错误!以下是十六进制转储的结果
$ od -tcx1 ~/windowlistener.clj
...
0000700 ( w i n d o w I c
20 20 20 20 20 20 20 28 77 69 6e 64 6f 77 49 63
0000720 o n i f i e d 342 200 213 [ t h i s
6f 6e 69 66 69 65 64 e2 80 8b 20 5b 74 68 69 73
0000740 e ] ) \t \n
那个342 200 213或e2 80 8b是什么东西?我猜文件使用的是UTF-8,在这种情况下,这些字节表示代码点0x200b(通过位操作[2]),它表示UNICODE中的“ZERO WIDTH SPACE”。[3]
(顺便说一下,我在Oracle的网页上没有找到这些字节。也许Firefox为了剪贴板的好处,在元素之间插入了ZERO WIDTH SPACE。)
我很惊讶编译器将ZERO WIDTH SPACE视为符号字符。这种行为与javac不同。根据Character,ZERO WIDTH SPACE是标识符可忽略的。
$ clj
Clojure 1.10.1
user=> (Character/isIdentifierIgnorable 0x200b)
true
UNICODE巧妙地建议“为了安全起见”缩小标识符字符集合[4],据信是因为用看不见的区别来编写代码是一种没有合法用途的力量。
确实,javac忽略了ZERO WIDTH SPACE:在“public static void main”中的“main”中间插入ZERO WIDTH SPACE,程序仍然可以正常工作。[5] 如果没有这样的先例,我可能更愿意直接拒绝“可忽略”的字符,但我会用代码检查器来实现。
而且,我抱怨错误消息没有清楚地说明问题。javac通过首先剔除标识符可忽略的字符来避开这个挑战,因此没有需要揭示的不可见方面。无论如何,并非所有不慎使用的ZERO WIDTH SPACE都会立即导致编译器错误。我从javadoc粘贴的代码中充满了它们,包括附在参数名上,这不会导致错误,直到以后——而且很可能,错误将涉及“正确”的拼写,而不是受感染的拼写。
总之,出于人道主义原因,我们能否建议Clojure通过忽略它们(如javac所做)或将它们视为错误来将“标识符可忽略”的字符排除在符号之外?
[1] https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/java/awt/event/WindowListener.html
[2] https://en.wikipedia.org/wiki/UTF-8#Encoding
[3] https://util.unicode.org/UnicodeJsps/character.jsp?a=200B
[4] http://unicode.org/reports/tr31/#Figure_Code_Point_Categories_for_Identifier_Parsing
[5] 可以使用 Unicode 字面量来完成
class A {
public static void m\u200bain(String[] args) throws Exception {
System.out.println("Hello world!");
}
}