8

Character/isWhitespace 的正确用法包括:

(Character/isWhitespace \a) => false
(Character/isWhitespace \ ) => true

但是,我的第一次尝试是这样,我发现错误令人困惑。

(Character/isWhitespace "")
  =>  IllegalArgumentException No matching method found: isWhitespace
  => clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:80)

IllegalArgument部分是有道理的,但为什么它说“找不到匹配的方法”?显然该功能确实存在。

澄清

我问这个问题的原因是我是 Clojure 的新手,并且认为我从根本上误解了一些东西。

当我输入(Character/isWhitespace \a)时,我我想说的是:“我知道有一个Character命名空间,其中有一个名为 的函数isWhitespace,我想调用该函数并传入\a”。

在这个心智模型上,我上面的结果令人困惑,因为 Clojure 似乎在说,“只要你给我一个这个函数不接受的参数类型,我就会假装这个函数不存在。” 例如,“你不能混合砖块,所以如果你尝试,我会给你一个 BlenderDoesntExist 错误。” 这很奇怪。

有些答案似乎暗示

  • 名称Character/isWhitespace只是Clojure 用于查找函数的一部分,另一部分是参数的类型(我做了更多的搜索:这可能是一种多方法吗?)
  • 正在查找 Java 类的方法?

一个很好的答案将为我澄清这个过程。

4

4 回答 4

10

tl;博士

clojure 编译器依赖反射来为 Java 互操作类方法找到匹配的签名,当找不到任何东西时,它会引发自己的异常。

在这种情况下, anIllegalArgumentException会正确引发,但noMethodReport会显示错误消息,从而导致混淆。

这是github repo上负责它的源代码。clojure

长版

一、Java互操作解析演练。

当 clojure 解析器找到.点宏时,HostExpr解析器会处理解析,尝试确定第二个参数是符号还是类。

如果它是一个类,则假定它是被调用的该类的静态方法,然后继续解析StaticMethodExpr

解析器内部的第一件事是尝试通过对类的反射来找到方法:

  List methods = Reflector.getMethods(c, args.count(), methodName, true);
  if(methods.isEmpty())
      throw new IllegalArgumentException("No matching method: " + methodName);

它正确地找到并且在这一点上没有引发异常

然后它将参数添加到找到的方法中:

  java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i);
  params.add(m.getParameterTypes());

之后尝试找到匹配的方法签名索引:

  methodidx = getMatchingParams(methodName, params, args, rets);

在这种情况下,它返回“-1”并method保持为空。这就是解析阶段。

评估时间...

然后当invokeStaticMethod被调用时,它会调用getMethods哪个Reflector.java正确地找到“isWhitespace”的两个匹配签名。

最后是您在函数内部看到的令人困惑的消息:

 static Object invokeMatchingMethod(String methodName, List methods, Object target, Object[] args)

对找到的方法进行参数匹配测试,以尝试找到具有正确签名的方法:

 for(Iterator i = methods.iterator(); i.hasNext();)
   {
    m = (Method) i.next();
    Class[] params = m.getParameterTypes();
    if(isCongruent(params, args))

如果没有找到匹配的签名,则会引发异常

if(m == null)
   throw new IllegalArgumentException(noMethodReport(methodName,target));

所以答案是 clojure 编译器依赖反射来为方法找到匹配的签名,并且当什么都没有找到时它会引发它自己的异常。

在这种情况下, anIllegalArgumentException会正确引发,但noMethodReport会显示错误消息,从而导致混淆。

于 2013-10-25T14:04:00.417 回答
7

Character/isWhitespace是 Java 方法(java.lang.Character类的静态方法)。有关调用 Java 方法的语法示例,请参阅Java 互操作。

常规 Clojure 函数仅由它们的名称定义,而 Java 方法由它们的签名定义,签名由它们的名称和参数的数量和类型组成。

Character类中isWhitespace定义的方法的唯一变体是和。因此,使用字符串调用时“没有匹配的方法” 。isWhitespace(char ch)isWhitespace(int codepoint)isWhitespace

于 2013-10-25T17:00:39.083 回答
3

因为“”是类型字符串,而不是字符。

    user=> (type \a)
    java.lang.Character
    user=> (type "")
    java.lang.String

字符串(java)类没有“isWhitespace”方法,而“字符”类有。你不能用螺丝刀钉钉子。

那里有一些提示:

如何在 Java Character 类中表示空字符

"" 是一个空字符串。但是没有“空”字符的表示。虽然字符“\0”确实:

      user=> (Character/isWhitespace \0)
      false

而 isWhitespace 字符的文档:

http://docs.oracle.com/javase/6/docs/api/java/lang/Character.html#isWhitespace%28char%29

说:

“从 U+0000 到 U+FFFF 的字符集有时称为基本多语言平面 (BMP)。码位大于 U+FFFF 的字符称为补充字符。Java 2 平台使用 UTF-16在 char 数组以及 String 和 StringBuffer 类中表示。在这种表示中,补充字符表示为一对 char 值,第一个来自高代理范围 (\uD800-\uDBFF),第二个来自低-代理范围 (\uDC00-\uDFFF)。" 空报价不属于哪个。

换句话说,isWhitespace 方法无法“理解”一个空字符串。

此外,来自 clojure java interlop doc http://clojure.org/java_interop

上面给出了访问字段或方法成员的首选惯用形式。实例成员表单适用于字段和方法。它们都在宏扩展时扩展为对点运算符的调用(如下所述)。

所以打字

      (Character/isWhitespace "")

扩展到

       (. Classname instanceMember instance args*) 

       (. Character isWhitespace "")

导致错误。

       user=>  (. Character isWhitespace "")
       java.lang.IllegalArgumentException: No matching method found: isWhitespace (NO_SOURCE_FILE:0)

虽然您必须查看 clojure 源代码才能确认,但我的猜测是来自可能在下面进行的动态 java 编译:

  public class toto {

     public toto ()
     {
        Character.isWhitespace("");
     }

  }

然后:

  javac toto.class

  toto.java:5: error: no suitable method found for isWhitespace(String)
  Character.isWhitespace("");
         ^
  method Character.isWhitespace(int) is not applicable
  (actual argument String cannot be converted to int by method invocation conversion)
  method Character.isWhitespace(char) is not applicable
  (actual argument String cannot be converted to char by method invocation conversion)
  1 error
于 2013-10-25T12:56:58.597 回答
2

没有像apiisWhitespace那样Character接受 String 作为参数的方法。isWhitespace方法只接受intorchar作为参数。

于 2013-10-25T12:56:19.960 回答