150

刚刚收到评论说我的方法的静态导入不是一个好主意。静态导入是来自 DA 类的方法,该类主要具有静态方法。因此,在业务逻辑的中间,我有一个显然似乎属于当前类的 da 活动:

import static some.package.DA.*;
class BusinessObject {
  void someMethod() {
    ....
    save(this);
  }
} 

审阅者并不热衷于我更改代码,我也没有,但我确实同意他的观点。没有静态导入的一个原因是它在定义方法的位置令人困惑,它不在当前类中,也不在任何超类中,因此识别它的定义需要一些时间(基于 Web 的评论系统没有可点击IDE 之类的链接 :-) 我真的不认为这很重要,静态导入仍然很新,很快我们都会习惯于定位它们。

但另一个我同意的原因是,不合格的方法调用似乎属于当前对象,不应该跳转上下文。但如果它真的属于,扩展那个超类是有意义的。

那么,什么时候静态导入方法才有意义呢?你什么时候做的?你/你喜欢不合格电话的样子吗?

编辑:流行的观点似乎是静态导入方法,如果没有人将它们混淆为当前类的方法。例如来自 java.lang.Math 和 java.awt.Color 的方法。但是如果 abs 和 getAlpha 不是模棱两可的,我不明白为什么 readEmployee 是。与许多编程选择一样,我认为这也是个人喜好。

4

16 回答 16

163

这是 Sun 发布该功能时的指南(强调原文):

那么什么时候应该使用静态导入呢?非常节俭!仅当您想要声明常量的本地副本或滥用继承(常量接口反模式)时才使用它。...如果您过度使用静态导入功能,它可能会使您的程序不可读和不可维护,并用您导入的所有静态成员污染其名称空间。您的代码的读者(包括您,在您编写代码几个月后)将不知道静态成员来自哪个类。从一个类中导入所有的静态成员对可读性尤其有害;如果您只需要一两个成员,请单独导入它们。

https://docs.oracle.com/javase/8/docs/technotes/guides/language/static-import.html

我想特别指出两个部分:

  • 当您想“滥用继承”时才使用静态导入。在这种情况下,您会想拥有 BusinessObjectextend some.package.DA吗?如果是这样,静态导入可能是一种更简洁的处理方式。如果您从未梦想过扩展some.package.DA,那么这可能是对静态导入的不良使用。不要仅仅在打字时使用它来保存几个字符。
  • 导入单个成员。import static some.package.DA.save而不是DA.*。这将使查找此导入方法的来源变得更加容易。

就个人而言,我很少使用这种语言特性而且几乎总是只使用常量或枚举,从不使用方法。对我来说,这种权衡几乎是不值得的。

于 2009-01-07T17:01:59.613 回答
67

静态导入的另一个合理用途是使用 JUnit 4。在早期版本的 JUnit 方法中,例如assertEqualsfail继承自测试类扩展junit.framework.TestCase.

// old way
import junit.framework.TestCase;

public class MyTestClass extends TestCase {
    public void myMethodTest() {
        assertEquals("foo", "bar");
    }
}

在 JUnit 4 中,测试类不再需要扩展TestCase,而是可以使用注解。然后,您可以从以下位置静态导入断言方法org.junit.Assert

// new way
import static org.junit.Assert.assertEquals;

public class MyTestClass {
    @Test public void myMethodTest() {
        assertEquals("foo", "bar");
        // instead of
        Assert.assertEquals("foo", "bar");
    }
}

JUnit文档以这种方式使用它。

于 2009-01-07T18:32:14.983 回答
30

Effective Java, Second Edition的第 19条末尾指出,如果您发现自己大量使用实用程序类中的常量,则可以使用静态导入。我认为这个原则适用于常量和方法的静态导入。

import static com.example.UtilityClassWithFrequentlyUsedMethods.myMethod;

public class MyClass {
    public void doSomething() {
        int foo = UtilityClassWithFrequentlyUsedMethods.myMethod();
        // Can be written less verbosely as
        int bar = myMethod();
    }
}

这有优点也有缺点。它以丢失有关方法定义位置的一些即时信息为代价使代码更具可读性。然而,一个好的 IDE 会让你去定义,所以这不是什么大问题。

您仍然应该谨慎使用它,并且仅当您发现自己多次使用导入文件中的内容时。

编辑:更新为更具体的方法,因为这就是这个问题所指的。无论导入什么(常量或方法),该原则都适用。

于 2009-01-07T18:20:53.150 回答
21

我认为静态导入对于在使用 utils 类(如ArraysAssertions.

不知道为什么,但罗斯跳过了他引用的文档中提到这一点的最后一句话。

使用得当,静态导入可以通过删除类名重复的样板来使您的程序更具可读性。

基本上从这个博客复制:https ://medium.com/alphadev-thoughts/static-imports-are-great-but-underused-e805ba9b279f

例如:

测试中的断言

这是我认为我们都同意的最明显的案例

Assertions.assertThat(1).isEqualTo(2);

// Use static import instead
assertThat(1).isEqualTo(2);

Utils 类和枚举

在使用 utils 类时,可以在许多情况下删除类名,从而使代码更易于阅读

List<Integer> numbers = Arrays.asList(1, 2, 3);

// asList method name is enough information
List<Integer> numbers = asList(1, 2, 3);

java.time 包有一些应该使用的情况

// Get next Friday from now, quite annoying to read
LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));

// More concise and easier to read
LocalDate.now().with(next(FRIDAY));

何时不使用的示例

// Ok this is an Optional
Optional.of("hello world");

// I have no idea what this is 
of("hello world");
于 2018-03-01T07:11:08.253 回答
17

我同意从可读性的角度来看它们可能存在问题,应该谨慎使用。但是当使用一个常见的静态方法时,它们实际上可以增加可读性。例如,在 JUnit 测试类中,诸如此类的方法assertEquals是显而易见的。类似的方法来自java.lang.Math.

于 2009-01-07T15:57:13.423 回答
12

我经常将它用于颜色。

static import java.awt.Color.*;

颜色不太可能与其他颜色混淆。

于 2009-01-07T16:35:07.887 回答
5

我建议在将 OpenGL 与 Java 一起使用时使用静态导入,这是属于“大量使用实用程序类中的常量”类别的用例

考虑到

import static android.opengl.GLES20.*;

允许您移植原始 C 代码并编写可读的内容,例如:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(samplerUniform, 0);
glBindBuffer(GL_ARRAY_BUFFER, vtxBuffer);
glVertexAttribPointer(vtxAttrib, 3, GL_FLOAT, false, 0, 0);

而不是那种普遍存在的丑陋:

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
GLES20.glUniform1i(samplerUniform, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vtxBuffer);
GLES20.glVertexAttribPointer(vtxAttrib, 3, GLES20.GL_FLOAT, false, 0, 0);
于 2016-03-19T23:24:53.643 回答
3

在将数学繁重的代码从 C/C++ 移植到 java 时,我使用“import static java.lang.Math.*”。数学方法将 1 映射到 1,并且在没有类名限定的情况下更容易区分移植的代码。

于 2014-10-30T16:37:04.727 回答
2

由于您刚才提到的问题,静态导入是我从未使用过且不打算使用的唯一 Java 的“新”功能。

于 2009-01-07T15:48:56.047 回答
2

谈论单元测试:大多数人对mocking框架提供的各种静态方法使用静态导入,例如when()verify().

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

当然,当使用唯一的断言时,您应该使用assertThat()它来静态导入所需的 hamcrest 匹配器,如下所示:

import static org.hamcrest.Matchers.*;
于 2018-05-25T13:16:22.577 回答
2

我发现这在使用实用程序类时非常方便。

例如,而不是使用:if(CollectionUtils.isNotEmpty(col))

我可以改为:

import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
if(isNotEmpty(col))

当我在代码中多次使用此实用程序时,哪个 IMO 增加了代码的可读性。

于 2018-11-09T18:09:12.087 回答
1

它们有助于减少冗长,特别是在调用大量导入方法的情况下,并且本地方法和导入方法之间的区别很明显。

一个例子:涉及对 java.lang.Math 的多次引用的代码

另一个:一个 XML 构建器类,其中将类名添加到每个引用会隐藏正在构建的结构

于 2009-01-07T15:57:48.010 回答
1

我认为静态导入对于 gettext 样式的 NLS 来说很简洁。

import static mypackage.TranslatorUtil._;

//...
System.out.println(_("Hello world."));

这既将字符串标记为必须提取的字符串,又提供了一种简单而干净的方法来用其翻译替换字符串。

于 2013-07-26T10:30:13.407 回答
1

IMO 静态导入是一个相当不错的功能。绝对正确的是,对静态导入的严重依赖使代码不可读,难以理解静态方法或属性属于哪个类。然而,根据我的经验,它成为一个有用的特性,尤其是在设计Util提供一些静态方法和属性的类时。每当提供静态导入时出现的歧义可以通过建立代码标准来规避。根据我在一家公司的经验,这种方法是可以接受的,并且可以使代码更清晰、更易于理解。_最好我在前面的静态方法和静态属性中插入字符(不知何故从C中采用). 显然,这种方法违反了 Java 的命名标准,但它使代码更加清晰。例如,如果我们有一个 AngleUtils 类:

public class AngleUtils {

    public static final float _ZERO = 0.0f;
    public static final float _PI   = 3.14f;

    public static float _angleDiff(float angle1, float angle2){

    }

    public static float _addAngle(float target, float dest){

    }
}

在这种情况下,静态导入提供了清晰性,并且代码结构对我来说看起来更优雅:

import static AngleUtils.*;

public class TestClass{

    public void testAngles(){

        float initialAngle = _ZERO;
        float angle1, angle2;
        _addAngle(angle1, angle2);
    }
}

马上有人可以分辨出哪个方法或属性来自静态导入​​,它隐藏了它所属的类的信息。我不建议对作为模块组成部分并提供静态和非静态方法的类使用静态导入,因为在这些情况下,了解哪个类提供某些静态功能很重要。

于 2014-08-25T16:16:21.693 回答
-1

您需要在以下情况下使用它们:

  • 您希望使用switch带有枚举值的语句
  • 你想让你的代码难以理解
于 2009-01-07T17:42:38.750 回答
-5

我尽可能使用它们。如果我忘记了,我有 IntelliJ 设置来提醒我。我认为它看起来比完全限定的包名称要干净得多。

于 2009-01-07T16:22:54.120 回答