为什么有很多非公开的低级Java方法以0结尾?

huangapple go评论68阅读模式
英文:

Why are there a lot of non-public low-level Java methods ending in 0?

问题

我喜欢用IntelliJ查看Java源代码。但是,我注意到了一些奇怪的地方。一些非公开方法,特别是那些位于低级类中的方法,会以0结尾。这在大多数情况下都是针对本地方法,然而我也注意到一些非本地方法也有这种名称。例如,java.lang.reflect.Executable#getAnnotatedReturnType0(Type)java.lang.reflect.AccessibleObject#setAccessible0(boolean)。前者仅设置两个布尔标志!这种奇怪的约定背后的原因是什么?

英文:

I like to poke around the Java source with IntelliJ. However, I have noticed something strange. Some non-public methods, particularly those in low-level classes, end in 0. This is most often the case with native methods, however I have observed some non-native methods with this name too. For example, java.lang.reflect.Executable#getAnnotatedReturnType0(Type), and java.lang.reflect.AccessibleObject#setAccessible0(boolean). The former simply sets two boolean flags! What is the reasoning behind this strange convention?

答案1

得分: 4

这是一个相对常见的辅助方法的约定。

Java不允许在方法内部再定义方法(尽管有一些关于添加此功能的讨论)。有时你会有一个重复的任务,但它又不适合放在循环结构中(比如while或for循环),也不容易放在lambda表达式中(因为它的参数和需要捕获的异常的要求与方便的类型不匹配,或者代码早于JDK8)。

JDK本身和一些其他项目使用以下约定:辅助方法与其辅助的方法名称相同,带有单个数字后缀,通常为0(如果有更多辅助方法,它们可能是1、2等)。

这在重载方法中特别常见。假设你有以下方法签名:

/**
 * 从虚拟计算机中分离磁盘。
 * 除非“force”参数为true,否则该磁盘在虚拟PC上必须已被卸载,
 * 但要注意(关于强制断开连接的风险的长篇故事在这里)。
 *
 * @param computerId 要从中分离磁盘的计算机。
 * @param diskId 要分离的磁盘的ID。
 * @param force 即使磁盘正在使用,也强制执行此操作。
 * @throws DiskInUseException 如果磁盘正在使用。如果“force”为true,则永远不会抛出此异常。
 */
public void detachDisk(
   String computerId, String diskId, boolean force) throws DiskInUseException { ... }

那是一个糟糕的API。它有各种丑陋的问题:

  • 文档中指定了许多注意事项,如果我将“force”设置为“false”,则这些注意事项根本不适用。
  • 它抛出一个可能永远不会发生的已检查异常,这总是不好的(显然不要强制调用代码捕获不可能的异常!)- 如果“force”为“true”,则不会发生“DiskInUseException”。
  • 如果我调用它:“detachDisk(computer, disk, true)”并且在代码中看到它,我可以猜出“computer”是指向虚拟PC的变量,对“disk”也是一样的,但是“true”可能是什么意思呢?我可能猜想它是关于强制的,但也许在我的脑海中,第三个参数是“safely”(与“force”的相反)。

我们可以通过使用枚举而不是布尔值来解决一些问题,但还有其他问题。这是更优秀的API设计:

/**
 * 相同的文本,但省略有关强制操作危险性的所有内容。
 * @see #forceDetachDisk(String, String)
 */
public void detachDisk(String computerId, String diskId)
   throws DiskInUseException {..}

/**
 * 相同,但现在强调强制操作的危险性。
 * @see #detachDisk(String, String)
 */
void forceDetachDisk(String computerId, String diskId) { .. }

好多了,但最有可能这两个方法共享大部分实现。这自然而然地导致了创建这个东西:

private void detachDisk0(String computerId, String diskId,
   boolean force) throws DiskInUseException { .. }

它是私有的,所以它的混淆和问题不重要,公共的detachDisk方法将简单地调用私有方法。然而,一些自动完成对话框,以及包括javac在内的大多数Java编译器,都会识别对私有方法的调用,并会“贴心地”提醒您它确实存在,您是否想改变它的访问修饰符(如果它是源文件而不是类依赖)?- 这不太好。在那里加上0以减轻影响,并且更清楚地表示私有方法仅意味着由不带0的方法调用,而不是其他任何方法,甚至不是在同一个源文件中的方法。

这是更多项目应该使用的API设计。

英文:

It's a somewhat common convention for helper methods.

Java does not allow methods-in-methods (though there is some traffic about adding them). Sometimes you have a repetitive task that nevertheless doesn't fit properly in a looping structure (such as a while or for loop) and doesn't easily fit in a lambda (because its parameter + checked exception requirements don't line up to a convenient type, or the code predates JDK8).

The JDK itself, and some other projects, use the convention of: helper methods get the same name it is a helper for, with a single digit suffix, generally 0 (if there are more helpers, they'd be 1, 2, etc).

This is particularly common with overloading. Let's say you have the following method signature:

/**
 * Detaches a disk from a virtual computer.
 * The disk must have been unmounted already on the virtual PC, unless
 * the {@code force} parmater is true, but note that (long story
 * about the perils of force disconnecting here).
 *
 * @param computerId computer to detach the disk from.
 * @param diskId ID of disk to detach
 * @param force Force the issue even if the disk is in use.
 * @throws DiskInUseException If the disk is in use. Never thrown if
 * {@code force} is {@code true}.
 */
public void detachDisk(
   String computerId, String diskId, boolean force) throws DiskInUseException { ... }

That's a bad API. It's got all sorts of ugly warts on it:

  • The docs specify a lot of caveats that literally do not apply whatsoever if I set force to false.
  • It throws a checked exception that may never occur, which is always bad (don't force calling code to catch impossible exceptions, obviously!) - if force is true, the DiskInUseException cannot occur.
  • If I call it: detachDisk(computer, disk, true) and I see that in code, I can guess that computer is a variable referring to a virtualPC, same for disk but whatever might true be about? I might guess that it's about forcing things, but maybe in my head that third parameter is safely (the reverse of force).

We can solve a few issues by using an enum instead of a boolean, but that leaves the rest. This is vastly superior API design:

/**
 * Same text, but leave out all the stuff about the dangers of forcing.
 * @see #forceDetachDisk(String, String)
 */
public void detachDisk(String computerId, String diskId)
   throws DiskInUseException {..}

/**
 * Same, but now highlight the dangers of forcing.
 * @see #detachDisk(String, String)
 */
void forceDetachDisk(String computerId, String diskId) { .. }

Muuuch better, but most likely these 2 methods share most of the implementation. That naturally leads to making this thing:

private void detachDisk0(String computerId, String diskId,
   boolean force) throws DiskInUseException { .. }

which is private so the confusion and warts it has don't matter, and the public detachDisk methods will simply call the private one. However, some auto-complete dialogs, and certainly most java compilers including javac itself, do recognize calls to private methods and will 'helpfully' alert you about the fact that it does exist, and would you perhaps like to change its accessor keyword (if it's a source file and not a class dep)? - That's not so nice. Toss that 0 in there to lessen the effect, and to make more clear that the private method is meant solely to be invoked by the 0-less methods and no other method; you wrote it with only its usage from the detachDisk method, and not any other method, not even in the same source file.

That's an API design more projects should be using.

答案2

得分: 0

这种“约定”不仅在Java中被使用过。我记得在C#中也看到过相同的情况。通常你会看到一个公有方法使用相同的名称(末尾没有零)来调用以零结尾的私有方法。

但无论如何,这种用于私有方法的命名约定不是一个好的示范,也不是你应该效仿的东西。

英文:

This "convention" has been used not only for Java. I remember seeing the same in C#. Usually you will see a public method with the same name (without the zero at the end) calling the private method ending in zero.

But anyway, this naming convention for a private method is not a good example or something you should copy.

huangapple
  • 本文由 发表于 2020年10月15日 08:34:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/64363262.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定