码农的世界

Chapter-4-Item-25-源文件仅限有单个顶层类

2018-11-18  本文已影响0人  MrDcheng

本文在已发布在GitHub(https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-25-Limit-source-files-to-a-single-top-level-class.md)欢迎纠错订正

While the Java compiler lets you define multiple top-level classes in a single source file, there are no benefits associated with doing so, and there are significant risks. The risks stem from the fact that defining multiple top-level classes in a source file makes it possible to provide multiple definitions for a class. Which definition gets used is affected by the order in which the source files are passed to the compiler. To make this concrete, consider this source file, which contains only a Main class that refers to members of two other top-level classes (Utensil and Dessert):

虽然Java编译器允许您在单个源文件中定义多个顶层类,但这样做没有任何好处,而且存在重大风险。这种风险源于这样一个事实,即在源文件中定义多个顶层类使得为一个类提供多个定义成为可能。所使用的定义受源文件传给编译器的顺序的影响。要使(这个问题)具体化,请考虑这个源文件,它只包含一个主类,该主类引用另外两个顶层类的成员(餐具和甜点):

public class Main {
  public static void main(String[] args) {
    System.out.println(Utensil.NAME + Dessert.NAME);
  }
}

Now suppose you define both Utensil and Dessert in a single source file named Utensil.java:

现在假设您在一个名为Utensil.java的源文件中定义了餐具和甜点:

// Two classes defined in one file. Don't ever do this!
// 在一个文件中定义两个类。永远不要这样做!
class Utensil {
  static final String NAME = "pan";
}

class Dessert {
  static final String NAME = "cake";
}

Of course the main program prints pancake. Now suppose you accidentally make another source file named Dessert.java that defines the same two classes:

当然,main方法应该输出pancake。现在假设您意外地制作了另一个名为Dessert的源文件。java定义了相同的两个类:

// Two classes defined in one file. Don't ever do this!
// 在一个文件中定义两个类。永远不要这样做!
class Utensil {
  static final String NAME = "pot";
}

class Dessert {
  static final String NAME = "pie";
}

If you’re lucky enough to compile the program with the command javac Main.java Dessert.java, the compilation will fail, and the compiler will tell you that you’ve multiply defined the classes Utensil and Dessert. This is so because the compiler will first compile Main.java, and when it sees the reference to Utensil (which precedes the reference to Dessert), it will look in Utensil.java for this class and find both Utensil and Dessert. When the compiler encounters Dessert.java on the command line, it will pull in that file too, causing it to encounter both definitions of Utensil and Dessert.

如果您足够幸运,使用javac Main.java Dessert.java命令编译程序时,编译将失败,编译器将告诉您多重定义了餐具和甜点。这是因为编译器将首先编译Main.java,当它看到对Utensil的引用(在对Dessert的引用之前)时,它将在Utensil.java中查找这个类,并找到餐具和甜点。当编译器在命令行上遇到Dessert.java时,(编译器)也会载入该文件,导致(编译器)同时遇到Utensil和Dessert的定义。

If you compile the program with the command javac Main.java or javac Main.java Utensil.java, it will behave as it did before you wrote the Dessert.java file, printing pancake. But if you compile the program with the command javac Dessert.java Main.java, it will print potpie. The behavior of the program is thus affected by the order in which the source files are passed to the compiler, which is clearly unacceptable.

如果您使用命令javac Main.javajavac Main.java Utensil.java编译程序,它的行为将与编写Dessert.java文件(打印pancake)之前一样。但是如果您使用命令javac Dessert.java Main.java编译程序,它将打印potpie。因此,程序的行为受到源文件传递给编译器的顺序的影响,这显然是不可接受的。

Fixing the problem is as simple as splitting the top-level classes (Utensil and Dessert, in the case of our example) into separate source files. If you are tempted to put multiple top-level classes into a single source file, consider using static member classes (Item 24) as an alternative to splitting the classes into separate source files. If the classes are subservient to another class, making them into static member classes is generally the better alternative because it enhances readability and makes it possible to reduce the accessibility of the classes by declaring them private (Item 15). Here is how our example looks with static member classes:

修复这个问题非常简单,只需将顶层类(在我们的示例中是餐具和甜点)分割为单独的源文件即可。如果您想将多个顶层类放到一个源文件中,请考虑使用静态成员类(Item-24)作为将类分割为单独的源文件的替代方法。如果(多个顶层类)隶属于另一个类,那么将它们转换成静态成员类通常是更好的选择,因为它增强了可读性,并通过声明它们为私有(Item-15),从而降低了类的可访问性。下面是我们的静态成员类示例的样子:

// Static member classes instead of multiple top-level classes
public class Test {

  public static void main(String[] args) {
    System.out.println(Utensil.NAME + Dessert.NAME);
  }

  private static class Utensil {
    static final String NAME = "pan";
  }

  private static class Dessert {
    static final String NAME = "cake";
  }
}

The lesson is clear: Never put multiple top-level classes or interfaces in a single source file. Following this rule guarantees(n. 保证;v. 保证,guarantee的第三人称单数) that you can’t have multiple definitions for a single class at compile time. This in turn guarantees that the class files generated by compilation, and the behavior of the resulting program, are independent of the order in which the source files are passed to the compiler.

教训很清楚:永远不要将多个顶层类或接口放在一个源文件中。遵循此规则可以确保在编译时单个类不能拥有多个定义。这反过来保证了编译所生成的类文件,以及程序的行为,是独立于源代码文件传递给编译器的顺序的。

上一篇下一篇

猜你喜欢

热点阅读