我正在学习Java,刚刚发现接口可以有字段,这些字段是公共静态和不可变的。到目前为止,我还没有看到任何关于这些的例子。这些接口常量的用例是什么,我能在Java标准库中看到一些吗?
java.io.ObjectStreamConstants
。这些接口应被视为异常情况,不应模仿。public final class Constants {
private Constants() {
// restrict instantiation
}
public static final double PI = 3.14159;
public static final double PLANCK_CONSTANT = 6.62606896e-34;
}
如果不想完全限定常量(即不想在常量前面加上类名前缀),可以使用静态导入(自Java 5起):
import static Constants.PLANCK_CONSTANT;
import static Constants.PI;
public class Calculations {
public double getReducedPlanckConstant() {
return PLANCK_CONSTANT / (2 * PI);
}
}
"常量接口模式是接口的不良使用"
无论这个假设的提出者是多么牛的大师,他/她都是基于需要继续高效地实现不良习惯和实践而构建这个假设的。这个假设基于推广不良软件设计习惯的有效性。
我在这里写了一篇回应反驳这个假设:什么是在Java中实现常量的最佳方式? 解释了这个假设毫无根据的基础。
这个问题已经持续了10年,直到我发表不支持这个误导性假设的理由后,只用了2小时就被关闭了,从而暴露了那些深深坚信这个错误假设的人们不愿意进行辩论。
以下是我对这个假设的表达:
持有这个假设的基础是需要方法和严格规则来应对不良软件习惯和方法所带来的影响。
这种观点的支持者无法提供除了那些受到不良习惯和实践影响的必要性之外的其他原因。
解决根本问题。
然后,为什么不充分利用和开发Java语言结构的每一个语言特性来方便自己。无需夹克。为什么要发明规则来对抗你们无效的生活方式,歧视和指责更有效的生活方式?
是信息组织。在工程或补充过程的解决方案之前,应该首先理解信息中介进程和行为,以及所谓的业务规则。几十年前这种信息组织方法被称为数据归一化。
只有将解决方案的组件的粒度和模块化与信息的组件的粒度和模块化对齐才是最佳策略,从而使解决方案的工程化成为可能。
组织信息存在两三个重要障碍:
缺乏对数据模型“规范化”需求的感知。
EF Codd关于数据规范化的陈述是有缺陷、有缺陷和含糊不清的。
最新的敏捷工程伪装成不需要提前计划和条件模块组织的错误观念,因为你可以在进行重构时进行。使用会计技巧延迟利润和资产化,因此将过程信息的基本发现视为不需要现在处理的东西。
不要制定规则或发布任何法令反对它,只因为你喜欢你的特别编写和运行程序的习惯。
不要以禁止枪支拥有为由,理由是有人不知道如何操作枪支或容易滥用枪支。
如果你所设计的规则只适用于无法专业编写代码的编程新手,并且你认为自己是其中的一员,请说明-不要声明你的法令适用于经过适当规范化的数据模型。
我不关心美国宪法的创始人对原意的看法。我不关心未经书面编写的意图。我只关心在书面宪法中所记录的内容,以及如何将它们利用于有效地运行社会。
我只关心Java语言/平台规范允许我做什么,我打算充分利用它们,为我提供一种有效和高效表达软件解决方案的介质。毋需外套。
它需要编写额外的代码来映射参数到值。Java的创始人没有提供参数值映射而要求你编写该映射代码的事实表明,枚举常量只是Java语言的不合适用法。
尤其是当你不被鼓励将参数规范化和组件化时,会给人一种假象,即混合到枚举容器中的参数属于同一维度。
不要忘记这一点。如果您设计并规范化了数据模型,并包括常量,则这些常量是合约。如果您没有规范化数据模型,则应遵守如何实践限制性编码以应对该坏习惯的法令。
因此,接口是实现常量合约的完美方式。
是的,任何人都可能意外地实现任何接口。没有什么能阻止这些失误的程序员。
不要制定限制性法令以保护导致未经合约/流浪参数进入API的假定不良实践。解决根本问题,而不是将责任归咎于接口常量。
一个正常运转和高效的程序员不在那里证明她可以在水下呆多长时间,在炎热的天气或湿润的雷雨中走多远。她要使用像汽车、公共汽车或至少自行车这样的高效工具,每天上班走10英里。
不要因为您对没有IDE编程的奇特禁欲主义迷恋而限制其他程序员。
OSGI就是这样一个框架。对接口常量的法令也是如此。
接口常量是将数据模型的经过良好设计和规范化的组件放入合约的有效和高效方式。
在类文件中嵌套适当命名的私有接口中的接口常量也是一个好的实践,可以将所有私有常量分组而不是散布到整个文件中。
int
字段与任何其他 int
常量进行比较,例如 if (userRole == Consts.MALE)...
或 if (userRole == Consts.US_ZIP_CODE)...
,但是使用枚举则更加安全。 - ALZ我现在已经多次遇到这个老问题了,但是被采纳的答案仍然让我困惑。经过很多思考,我认为这个问题还可以进一步澄清。
只需要比较它们:
public final class Constants {
private Constants() {
// restrict instantiation
}
public static final double PI = 3.14159;
public static final double PLANCK_CONSTANT = 6.62606896e-34;
}
对比
public interface Constants {
double PI = 3.14159;
double PLANCK_CONSTANT = 6.62606896e-34;
}
同样的用法,代码量更少。
我认为@Pascal Thivent的回答强调有误,以下是我的版本:
将静态成员放入接口中(并实现该接口)是一种不好的实践。
《Effective Java》中的引用假设常量接口由其他人实现,我认为这不应该(也不会)发生。
当您创建一个名为Constants
之类的常量接口时,它不应被任何人实现。(虽然在技术上是可能的,但这是唯一的问题)
标准库不能承受任何设计上的可能误用,因此您在标准库中看不到这样的东西。
然而,对于普通开发者的日常项目来说,使用常量接口要容易得多,因为您无需担心static
,final
,空构造函数
等,并且它不会导致任何错误的设计问题。我能想到的唯一缺点是它仍然具有“接口”的名称,但除此之外没有其他问题。
最后,我认为每个人都只是引用书籍,并对他们的立场提出意见和理由。我也不例外。也许决定仍取决于每个项目的开发人员。如果您感到舒适,就继续使用吧。我们所能做的最好的事情就是使它在整个项目中保持一致。
public
也可以省略,因为它是一个接口,使其变成简单的 double PI = 3.14159;
。使用 Constants.PI
不需要使用此类实现 Constants
接口!我认为接口方法在使用方面更加清晰,个人观点。 - krozaine有些回答非常合理。
但是我对这个问题有一些想法。(可能是错误的)
在我看来,接口中的字段不应该是整个项目的常量,它们只是接口和扩展接口以及实现这些接口或与它们有密切关系的类的手段。它们应该在特定范围内使用而不是全局。
关于接口的两点:
接口描述了实现它的对象所能做的 子集。(这是本质)
接口描述了被实现对象所遵循的常量规范。
所以我想如果不用常量接口来定义全局常量,那么就可以接受:
implement
它(当然,在实现中使用这些公共常量)。例子:
interface Drawable {
double GOLDEN_RATIO = 1.618033988;
double PI = 3.141592653;
...
// methods
...
}
public class Circle implements Drawable {
...
public double getCircumference() {
return 2 * PI * r;
}
}
void usage() {
Circle circle = new Circle(radius: 3.0);
double maxRadius = 5.0;
if ( circle.getCircumference() < 2 * Circle.PI * maxRadius ) {
...
}
}
Circle implements Drawable
,你立即知道Circle
可能符合在Drawable
中定义的常量,否则他们必须选择更糟糕的名称,因为好的名称PI
和GOLDEN_RATIO
已经被使用了!Drawable
接口的对象才符合在Drawable
中定义的特定精度的PI
和GOLDEN_RATIO
,还可以存在不是Drawable
接口的对象,其π和黄金分割比可能具有不同的精度。public interface Name {
String MANNY = "Manny";
String MOE = "Moe";
String JACK = "Jack";
String getName();
}
...可以提供字符串常量,这些常量可以用于类似以下的枚举常量中:
public enum PepBoys implements Name {
BOY1(MANNY),
BOY2(MOE),
BOY3(JACK);
private String name;
PepBoys(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
}
注解属性的值必须是常量值,而(具有讽刺意味的是)枚举常量在此上下文中不符合“常量”的要求。然而,在接口中定义的字符串常量确实符合要求:
@MyAnnotation(PepBoys.MANNY)
public void annotatedMethod() {
...
}
javax.swing.SwingConstants
接口是一个例子,其中包含了在 swing 类中使用的静态字段。这使您可以轻松地使用以下内容:
this.add(LINE_START, swingcomponent);
this.add(this.LINE_START, swingcomponent);
或者 this.add(SwingComponents.LINE_START, swingcomponent);
然而,该接口没有方法...