静态导入方法的好用例是什么?

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

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

评论家不希望我修改代码,我也没有,但我确实有点同意他的看法。不进行静态导入的一个原因是,方法的定义很混乱,它不在当前类中,也不在任何超类中,所以需要一些时间来确定它的定义(基于 Web 的评审系统没有像 IDE 那样的可点击链接: ——)我真的不认为这有什么关系,静态导入仍然很新,很快我们就会习惯于定位它们。

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

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

编辑: 流行的观点似乎是,如果没有人会把静态导入方法当作当前类的方法来混淆,那么静态导入方法就是。例如来自 java.lang 的方法。数学和爪哇。颜色。但是如果 abs 和 getAlpha 没有歧义,我就不明白为什么 readEmployee 有歧义。在很多编程选择中,我认为这也是一个个人偏好的问题。

108114 次浏览

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

我同意,从可读性的角度来看,它们可能是有问题的,应该谨慎使用。但是,当使用一个常见的静态方法时,它们实际上可以提高可读性。例如,在 JUnit 测试类中,像 assertEquals这样的方法显然是从哪里来的。对于来自 java.lang.Math的方法也是如此。

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

一个例子: 包含对 java.lang.Math 的多个引用的代码

另一个: XML 生成器类,在每个引用前面加上类名可以隐藏正在构建的结构

我一有机会就用。我有 IntelliJ 设置提醒我,如果我忘记了。我认为它看起来比一个完全限定的软件包名称干净得多。

我经常用它来做颜色。

static import java.awt.Color.*;

这是非常不可能的颜色将混淆与其他东西。

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

那么什么时候应该使用静态导入呢?非常节省!只有在您想声明常量的本地副本或滥用继承(Constant Interface Antipattern)时才使用它。如果过度使用静态导入特性,会使程序变得不可读和不可维护,导入的所有静态成员会污染程序的名称空间。您的代码的读者(包括您,在您编写代码几个月后)将不知道静态成员来自哪个类。从一个类中导入所有静态成员对可读性尤其有害; 如果只需要一个或两个成员,则分别导入它们。

(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.*。这将使得查找这个导入的方法来自何处变得更加容易。

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

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

  • 您希望使用带枚举值的 switch语句
  • 您希望使您的代码难以理解

有效的 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 可以让您进入定义,所以这不是什么大问题。

您仍然应该谨慎地使用它,并且只有在您发现自己多次使用导入文件中的内容时才应该这样做。

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

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

// old way
import junit.framework.TestCase;


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

在 JUnit4中,测试类不再需要扩展 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 文件以这种方式使用它。

我认为静态导入对于 gettext 风格的 NLS 来说是很好的。

import static mypackage.TranslatorUtil._;


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

这两种方法都将字符串标记为必须提取的字符串,并提供了一种简单而干净的方法来用转换替换字符串。

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);
}
}

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

我用的是 import static java.lang。数学。当从 C/C + + 向 Java 移植数学代码时 *’。这些数学方法映射为1到1,使得在没有类名限定的情况下对移植代码进行区分变得更加容易。

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

考虑一下

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);

我认为当使用像 ArraysAssertions这样的 utils 类时,静态导入对于删除冗余的类名是非常有用的。

不知道为什么,罗斯跳过了在 他所引用的文件中提到的最后一句话。

如果使用得当,静态导入可以删除重复类名的样板,从而使程序更具可读性。

基本上抄自这个博客: 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 类使代码更容易阅读时,可以删除类名

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


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

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");

谈到单元测试: 大多数人对 嘲笑框架提供的各种静态方法(如 when()verify())使用静态导入。

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

当然,当使用一个并且是唯一的断言时,您应该使用 assertThat(),这样就可以方便地静态导入所需的 Hamcrest 匹配器,比如:

import static org.hamcrest.Matchers.*;

我发现这在使用 Utilityclass 时非常方便。

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

相反,我可以:

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

当我在我的代码中多次使用这个工具时,IMO 增加了代码的可读性。