1. 概述

在这个教程中,我们将讨论在 Java 中使用通配符导入的优势和劣势。

2. Java 的导入

Java 的 import 语句声明代码中使用的名称(类名、静态变量和方法名)的来源。

例如,让我们看一个 Book 类:

import java.util.Date;
import java.util.List;
import java.util.UUID;

public class Book {

    private UUID id;

    private String name;

    private Date datePublished;

    private List<String> authors;
}

在这里,我们需要导入 DateUUID 的两种数据类型以及 List 接口,因为它们默认情况下不可用。因此,我们写三个导入语句来使这些数据类型在我们的类中可用。我们将这类导入称为特定导入。

3. Java 通配符导入

通配符导入指的是导入一个,而不是声明从包中使用的特定类名。

使用通配符,我们可以将上一个示例中的三个导入语句替换为一个语句:

import java.util.*;

public class Book {

    private UUID id;

    private String name;

    private Date datePublished;

    private List<String> authors;
}

这个通配符 import 语句将整个 java.util 包添加到搜索路径中,其中可以找到所需的 UUIDDateList 名称。

4. 通配符导入的优势

自然地,与特定导入相比,通配符导入在 Java 中有一些优势。让我们在下面的小节中讨论主要优势。

4.1. 清洁的代码

通配符导入有助于我们在代码中避免长长的导入列表。因此,这会影响代码的可读性,因为读者可能需要在每个源代码文件中滚动很多才能到达显示逻辑的部分。毫无疑问,更易读的代码也是清洁代码

这种观点也得到了 Robert C. Martin 在《Clean Code》一书中的支持。实际上,这本书建议在从同一源使用多个类时使用通配符导入。换句话说,当我们从同一个包导入两个或更多的类时,最好导入整个包。

4.2. 重构的便利性

使用通配符导入,重构更加容易。例如,当重命名一个类时,我们不需要移除所有特定导入声明。

此外,如果我们把一个类从一个包移动到另一个自己的包中,如果文件中已经存在针对这两个包的通配符导入,我们就不需要重构任何代码。

4.3. 松耦合

通配符导入促进了现代软件开发中的松耦合方法。

根据 Robert C. Martin 的观点,使用通配符导入的理念促进了松耦合。使用特定导入,类必须存在于一个包中。然而,使用通配符导入,特定的类不需要存在于包中。实际上,通配符导入将指定的包添加到搜索路径中,以便查找所需的类名。

因此,使用通配符风格的导入不会对包产生真正的依赖。

5. 通配符导入的劣势

通配符导入也有其劣势。接下来,我们将看看通配符导入如何导致一些问题。

5.1. 类名冲突

不幸的是,当通过通配符导入的包中存在多个类名时,冲突可能会发生。

在这种情况下,编译器会注意到有两个 Date 类,并由于在 java.sqljava.util 包中都找到了 Date 类而给出错误:

import java.util.*;
import java.sql.*;

public class Library {

    private UUID id;

    private String name;

    private Time openingTime;

    private Time closingTime;

    private List<Date> datesClosed;
}

为了防止此类错误,我们可以指定冲突类的期望来源。

为了解决上述示例中的错误,可以在现有的两个导入语句后面添加一行,指定冲突 Date 类的来源:

import java.util.*;
import java.sql.*;
import java.sql.Date;

5.2. 随机出现的类名冲突

有趣的是,随着时间的推移,冲突也可能出现,比如当我们在使用的另一个包的新版本中添加了一个类。

例如,在 Java 1.1 中,List 类仅在 java.awt 包中可用。然而,随着 Java 1.2 的发布,java.util 包中添加了一个名为 List 的接口。

让我们看一个例子:

import java.awt.*;
import java.util.*;

public class BookView extends Frame {

    private UUID id;

    private String name;

    private Date datePublished;

    private List<String> authors;
}

最终,当同时导入 java.awtjava.util 包作为通配符导入时,这种情况可能导致潜在冲突。因此,当我们迁移代码到更高版本的 Java 时,可能会遇到问题。