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


137

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

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

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

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

所以,当是有意义的静态导入的方法呢?你什么时候做的?您是否喜欢不合格的电话的外观?

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

多谢您的回覆,我想解决这个问题。


2
这是静态导入的很好用法:ibm.com/developerworks/library/j-ft18
intrepidis 2013年

1
@ mr5的语法是import static,功能是static import
Miserable Variable

Answers:


150

这是从Sun发布该功能时的指南中得出的(原始内容为重点):

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

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

我想特别指出两个部分:

  • 当您试图“滥用继承” 时才使用静态导入。在这种情况下,您是否会想拥有BusinessObject extend some.package.DA?如果是这样,静态导入可能是一种更清洁的方式来处理此问题。如果您从未梦想过扩展some.package.DA,那么这可能是对静态导入的不充分使用。键入时不要仅使用它来保存一些字符。
  • 导入个人成员。import static some.package.DA.save而不是DA.*。这样可以更轻松地找到此导入方法的来源。

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


9
同意 我偶尔使用静态导入工具,因为它们实际上使代码更容易遵循。
尼尔·科菲

65

静态导入的另一个合理用法是JUnit4。在早期版本的JUnit方法(例如assertEquals和中)fail是自test类扩展以来继承的junit.framework.TestCase

// old way
import junit.framework.TestCase;

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

在JUnit 4中,测试类不再需要扩展,TestCase而可以使用注释。然后,您可以从静态导入assert方法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 文档以这种方式使用它。


4
我同意 简化测试用例是一个意图很容易被误解的地方。
比尔·米歇尔

6
我们在项目中遇到了这个问题,实际上,人们在使用assert()时遇到了问题,并错误地认为这是由于他们对Assert包的静态导入而来的。一旦发现此问题,在我们的测试中对代码库的快速扫描发现了大约30个实例,这意味着在执行测试框架时未运行30个断言,因为在运行测试时未设置DEBUG标志。
克里斯·威廉姆斯

27

第19项末尾的有效Java第二版中指出,如果发现自己大量使用实用程序类中的常量,则可以使用静态导入。我认为该原理将适用于常量和方法的静态导入。

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将使您进入定义,因此这不是什么大问题。

仅当发现自己多次使用导入文件中的内容时,仍应谨慎使用此功能。

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


1
我的问题是关于静态导入方法,而不是字段。
悲惨的变量,

7
也许UtilityClassWithFrequentlyUsedMethods需要缩短。
Steve Kuo 2012年


@ Rob-Hruska如果计划频繁使用静态导入方法或字段,那么我是否可以将其包装在新方法或字段中?那会让我不静态导入吗?如:double myPI = Math.PI;然后我可以继续引用myPI而不是Math.PI
阿卜杜勒2014年

@Abdul-是的,你可以做到。
罗布·赫鲁斯卡

14

我同意从可读性的角度来看它们可能会出现问题,因此应谨慎使用。但是,当使用通用的静态方法时,它们实际上可以提高可读性。例如,在JUnit测试类中,类似assertEquals的方法从何而来是显而易见的。同样适用于的方法java.lang.Math


5
看到Math.round(d)与round(d)有什么不好?
郭富城2009年

5
@SteveKuo-出于同样的原因,数学家在处理公式时使用一个字母的变量名:有时候,较长的名称会干扰整个语句的可读性。考虑一个涉及多个三角函数的公式。一个易于掌握的数学公式:sin x cos y + cos x sin y。在Java中成为:Math.sin(x) * Math.cos(y) + Math.cos(x) * Math.sin(y)。可怕的阅读。
ToolmakerSteve

@ToolmakerSteve,这就是为什么我using这么想念C ++中的指令的原因:它们可以是local
富兰克林·于

11

我认为在使用utils类(如Arrays和)时,静态导入对于删除多余的类名确实很有用Assertions

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

通过适当地使用,静态导入可以消除类名重复的样板,从而使您的程序更具可读性。

基本上从此博客复制: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);

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

10

我经常使用它作为颜色。

static import java.awt.Color.*;

颜色极不可能与其他东西混淆。


1
这是我见过的最好的用例之一,它与旧的JUnit / Hamcrest / TestNG不同。
kevinarpe

3

我建议在将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);

2

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


谢谢邦贝。好吧,我确实相信它们更有意义,那就是必须扩展和连接只包含一堆静态决赛的接口。
悲惨变量

2

从C / C ++向Java移植数学运算代码时,我使用“导入静态java.lang.Math。*”。数学方法将1映射为1,并且无需类名限定即可轻松区分移植的代码。


2

我发现使用Utility类时这非常方便。

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

我可以改为:

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

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


2

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

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

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

import static org.hamcrest.Matchers.*;

1

它们对于减少废语很有用,特别是在有很多导入方法被调用且本地方法和导入方法之间的区别很明显的情况下。

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

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


1

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

import static mypackage.TranslatorUtil._;

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

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


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

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


感谢您的建议重新命名。顺便说一句,在某些环境中,传统上在前面使用下划线来命名私有方法/字段。我正在考虑修改约定,例如H_Helper我拥有的实用程序类或C_for CommonU_for 导入Utility。另外,我考虑为这些广泛使用的类使用一个或两个字符类名称,但担心这些名称有时会与本地名称冲突-使用一些带有大写方法名称的旧代码。
制造商史蒂夫(Steve)2015年

-1

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

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

9
这不是真的。(1)您可以很好地使用枚举常量,而无需静态导入它们。(2)例如,JUnit Assert类方法的静态导入很清楚。“ assertTrue(...)”与“ Assert.assertTrue(...)”一样可读,也许更多。
艾伦·克鲁格

5
如果在500行类中有5个静态导入,则很难分辨方法的来源。
davetron5000

4
+1,当您希望使代码难以理解时:)
悲惨的变量,2010年

-5

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


13
您正在考虑定期进口。静态导入使您可以引用类的成员,而无需使用类名对其进行限定,例如,静态导入java.lang.system.out;。out.println(“ foo”); //代替System.out.println(“ foo”);
sk。

现在,这是对静态导入的一个很好的解释……太糟糕了,我无法为评论+1
Eldelshell
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.