java中用于共享“常量”的静态字段接口
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/320588/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Interfaces with static fields in java for sharing 'constants'
提问by kitsune
I'm looking at some open source Java projects to get into Java and notice a lot of them have some sort of 'constants' interface.
我正在查看一些开源 Java 项目以进入 Java 并注意到其中很多都有某种“常量”接口。
For instance, processing.orghas an interface called PConstants.java, and most other core classes implement this interface. The interface is riddled with static members. Is there a reason for this approach, or is this considered bad practice? Why not use enums where it makes sense, or a static class?
例如,processing.org有一个名为PConstants.java的接口,大多数其他核心类都实现了这个接口。接口充满了静态成员。这种方法是否有原因,或者这被认为是不好的做法?为什么不在有意义的地方使用枚举或静态类?
I find it strange to use an interface to allow for some sort of pseudo 'global variables'.
我觉得使用接口来允许某种伪“全局变量”很奇怪。
public interface PConstants {
// LOTS OF static fields...
static public final int SHINE = 31;
// emissive (by default kept black)
static public final int ER = 32;
static public final int EG = 33;
static public final int EB = 34;
// has this vertex been lit yet
static public final int BEEN_LIT = 35;
static public final int VERTEX_FIELD_COUNT = 36;
// renderers known to processing.core
static final String P2D = "processing.core.PGraphics2D";
static final String P3D = "processing.core.PGraphics3D";
static final String JAVA2D = "processing.core.PGraphicsJava2D";
static final String OPENGL = "processing.opengl.PGraphicsOpenGL";
static final String PDF = "processing.pdf.PGraphicsPDF";
static final String DXF = "processing.dxf.RawDXF";
// platform IDs for PApplet.platform
static final int OTHER = 0;
static final int WINDOWS = 1;
static final int MACOSX = 2;
static final int LINUX = 3;
static final String[] platformNames = {
"other", "windows", "macosx", "linux"
};
// and on and on
}
采纳答案by Dan Dyer
It's generally considered bad practice. The problem is that the constants are part of the public "interface" (for want of a better word) of the implementing class. This means that the implementing class is publishing all of these values to external classes even when they are only required internally. The constants proliferate throughout the code. An example is the SwingConstantsinterface in Swing, which is implemented by dozens of classes that all "re-export" allof its constants (even the ones that they don't use) as their own.
这通常被认为是不好的做法。问题是常量是实现类的公共“接口”(为了更好的词)的一部分。这意味着实现类将所有这些值发布到外部类,即使它们只在内部需要。常量在整个代码中激增。一个例子是Swing 中的SwingConstants接口,它是由几十个类实现的,这些类都“重新导出”了它的所有常量(甚至是它们不使用的常量)作为自己的常量。
But don't just take my word for it, Josh Bloch also saysit's bad:
但不要只相信我的话,Josh Bloch 也说这很糟糕:
The constant interface pattern is a poor use of interfaces.That a class uses some constants internally is an implementation detail. Implementing a constant interface causes this implementation detail to leak into the class's exported API. It is of no consequence to the users of a class that the class implements a constant interface. In fact, it may even confuse them. Worse, it represents a commitment: if in a future release the class is modified so that it no longer needs to use the constants, it still must implement the interface to ensure binary compatibility. If a nonfinal class implements a constant interface, all of its subclasses will have their namespaces polluted by the constants in the interface.
常量接口模式是接口使用不当。一个类在内部使用一些常量是一个实现细节。实现一个常量接口会导致这个实现细节泄漏到类的导出 API 中。类实现常量接口对类的用户没有任何影响。事实上,它甚至可能使他们感到困惑。更糟糕的是,它代表了一种承诺:如果在将来的版本中修改了类,使其不再需要使用常量,它仍然必须实现接口以确保二进制兼容性。如果一个非final类实现了一个常量接口,那么它的所有子类的命名空间都会被接口中的常量污染。
An enum may be a better approach. Or you could simply put the constants as public static fields in a class that cannot be instantiated. This allows another class to access them without polluting its own API.
枚举可能是更好的方法。或者您可以简单地将常量作为公共静态字段放在无法实例化的类中。这允许另一个类访问它们而不会污染它自己的 API。
回答by gizmo
This came from a time before Java 1.5 exists and bring enums to us. Prior to that, there was no good way to define a set of constants or constrained values.
这来自 Java 1.5 存在之前的时间,并为我们带来了枚举。在此之前,没有定义一组常量或约束值的好方法。
This is still used, most of the time either for backward compatibility or due to the amount of refactoring needed to get rid off, in a lot of project.
在很多项目中,这仍然被使用,大部分时间要么是为了向后兼容,要么是由于需要大量重构才能摆脱。
回答by Zarkonnen
Instead of implementing a "constants interface", in Java 1.5+, you can use static imports to import the constants/static methods from another class/interface:
在 Java 1.5+ 中,您可以使用静态导入从另一个类/接口导入常量/静态方法,而不是实现“常量接口”:
import static com.kittens.kittenpolisher.KittenConstants.*;
This avoids the ugliness of making your classes implement interfaces that have no functionality.
这避免了使您的类实现没有功能的接口的丑陋。
As for the practice of having a class just to store constants, I think it's sometimes necessary. There are certain constants that just don't have a natural place in a class, so it's better to have them in a "neutral" place.
至于有一个类只是为了存储常量的做法,我认为有时是必要的。有些常量在类中没有自然的位置,因此最好将它们放在“中立”的位置。
But instead of using an interface, use a final class with a private constructor. (Making it impossible to instantiate or subclass the class, sending a strong message that it doesn't contain non-static functionality/data.)
但是不要使用接口,而是使用带有私有构造函数的 final 类。(使类无法实例化或子类化,发送一个强烈的信息,即它不包含非静态功能/数据。)
Eg:
例如:
/** Set of constants needed for Kitten Polisher. */
public final class KittenConstants
{
private KittenConstants() {}
public static final String KITTEN_SOUND = "meow";
public static final double KITTEN_CUTENESS_FACTOR = 1;
}
回答by Corky Cartwright
Given the advantage of hindsight, we can see that Java is broken in many ways. One major failing of Java is the restriction of interfaces to abstract methods and static final fields. Newer, more sophisticated OO languages like Scala subsume interfaces by traits which can (and typically do) include concrete methods, which may have arity zero (constants!). For an exposition on traits as units of composable behavior, see http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf. For a short description of how traits in Scala compare with interfaces in Java, see http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5. In the context of teaching OO design, simplistic rules like asserting that interfaces should never include static fields are silly. Many traits naturally include constants and these constants are appropriately part of the public "interface" supported by the trait. In writing Java code, there is no clean, elegant way to represent traits, but using static final fields within interfaces is often part of a good workaround.
考虑到事后的优势,我们可以看到 Java 在很多方面都被破坏了。Java 的一个主要失败是将接口限制为抽象方法和静态最终字段。更新、更复杂的 OO 语言(如 Scala)通过特征包含接口,这些特征可以(并且通常确实)包括具体方法,这些方法可能具有零元(常量!)。有关特征作为可组合行为单位的说明,请参阅http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf。有关 Scala 中的特征与 Java 中的接口的比较的简短描述,请参阅http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5. 在教授面向对象设计的上下文中,诸如断言接口不应包含静态字段之类的简单规则是愚蠢的。许多 trait 自然包含常量,而这些常量是 trait 支持的公共“接口”的适当组成部分。在编写 Java 代码时,没有干净、优雅的方式来表示特征,但在接口中使用静态 final 字段通常是一个很好的解决方法的一部分。
回答by pleerock
I do not pretend the right to be right, but lets see this small example:
我不假装正确,但让我们看看这个小例子:
public interface CarConstants {
static final String ENGINE = "mechanical";
static final String WHEEL = "round";
// ...
}
public interface ToyotaCar extends CarConstants //, ICar, ... {
void produce();
}
public interface FordCar extends CarConstants //, ICar, ... {
void produce();
}
// and this is implementation #1
public class CamryCar implements ToyotaCar {
public void produce() {
System.out.println("the engine is " + ENGINE );
System.out.println("the wheel is " + WHEEL);
}
}
// and this is implementation #2
public class MustangCar implements FordCar {
public void produce() {
System.out.println("the engine is " + ENGINE );
System.out.println("the wheel is " + WHEEL);
}
}
ToyotaCar doesnt know anything about FordCar, and FordCar doesnt know about ToyotaCar. principle CarConstants should be changed, but...
丰田汽车对福特汽车一无所知,福特汽车也不了解丰田汽车。原则 CarConstants 应该改变,但是......
Constants should not be changed, because the wheel is round and egine is mechanical, but... In the future Toyota's research engineers invented electronic engine and flat wheels! Lets see our new interface
常数不应该改变,因为轮子是圆的,发动机是机械的,但是…… 未来丰田的研究工程师发明了电子发动机和平轮!让我们看看我们的新界面
public interface InnovativeCarConstants {
static final String ENGINE = "electronic";
static final String WHEEL = "flat";
// ...
}
and now we can change our abstraction:
现在我们可以改变我们的抽象:
public interface ToyotaCar extends CarConstants
to
到
public interface ToyotaCar extends InnovativeCarConstants
And now if we ever need to change the core value if the ENGINE or WHEEL we can change the ToyotaCar Interface on abstraction level, dont touching implementations
现在,如果我们需要更改 ENGINE 或 WHEEL 的核心值,我们可以在抽象级别更改 ToyotaCar 接口,而不是触及实现
Its NOT SAFE, I know, but I still want to know that do you think about this
它不安全,我知道,但我仍然想知道你是否考虑过这个
回答by om39a
According to JVM specification, fields and methods in a Interface can have only Public, Static, Final and Abstract. Ref from Inside Java VM
根据 JVM 规范,接口中的字段和方法只能有 Public、Static、Final 和 Abstract。引用自 Java VM 内部
By default, all the methods in interface is abstract even tough you didn't mention it explicitly.
默认情况下,接口中的所有方法都是抽象的,即使您没有明确提及它也是如此。
Interfaces are meant to give only specification. It can not contain any implementations. So To avoid implementing classes to change the specification, it is made final. Since Interface cannot be instantiated, they are made static to access the field using interface name.
接口仅用于提供规范。它不能包含任何实现。所以为了避免实现类来改变规范,它是最终的。由于接口无法实例化,因此它们被设为静态以使用接口名称访问该字段。
回答by Loek Bergman
I do not have enough reputation to give a comment to Pleerock, therefor do I have to create an answer. I am sorry for that, but he put some good effort in it and I would like to answer him.
我没有足够的声誉来评论 Pleerock,因此我必须创建一个答案。对此我很抱歉,但他为此付出了一些努力,我想回答他。
Pleerock, you created the perfect example to show why those constants should be independent from interfaces and independent from inheritance. For the client of the application is it not important that there is a technical difference between those implementation of cars. They are the same for the client, just cars. So, the client wants to look at them from that perspective, which is an interface like I_Somecar. Throughout the application will the client use only one perspective and not different ones for each different car brand.
Pleerock,你创建了一个完美的例子来说明为什么这些常量应该独立于接口和继承。对于应用程序的客户端来说,这些汽车的实现之间存在技术差异并不重要。它们对客户来说是一样的,只是汽车。所以,客户想从这个角度来看待它们,这是一个像 I_Somecar 这样的接口。在整个应用程序中,客户将只使用一种视角,而不是针对每个不同的汽车品牌使用不同的视角。
If a client wants to compare cars prior to buying he can have a method like this:
如果客户想在购买前比较汽车,他可以有这样的方法:
public List<Decision> compareCars(List<I_Somecar> pCars);
An interface is a contract about behaviour and shows different objects from one perspective. The way you design it, will every car brand have its own line of inheritance. Although it is in reality quite correct, because cars can be that different that it can be like comparing completely different type of objects, in the end there is choice between different cars. And that is the perspective of the interface all brands have to share. The choice of constants should not make this impossible. Please, consider the answer of Zarkonnen.
界面是关于行为的契约,从一个角度显示不同的对象。你设计它的方式,每个汽车品牌都会有自己的传承路线。虽然这在现实中很正确,因为汽车可以是不同的,就像比较完全不同类型的物体一样,最终在不同的汽车之间有选择。这就是所有品牌必须共享的界面视角。常数的选择不应使这成为不可能。请考虑Zarkonnen的回答。
回答by phil_20686
There is a lot of hate for this pattern in Java. However, an interface of static constants does sometimes have value. You need to basically fulfill the following conditions:
在 Java 中有很多人讨厌这种模式。然而,静态常量的接口有时确实有价值。您需要基本满足以下条件:
The concepts are part of the public interface of several classes.
Their values might change in future releases.
- Its critical that all implementations use the same values.
这些概念是几个类的公共接口的一部分。
它们的值可能会在未来的版本中发生变化。
- 所有实现都使用相同的值至关重要。
For example, suppose that you are writing an extension to a hypothetical query language. In this extension you are going to expand the language syntax with some new operations, which are supported by an index. E.g. You are going to have a R-Tree supporting geospatial queries.
例如,假设您正在为一种假设的查询语言编写扩展。在此扩展中,您将使用索引支持的一些新操作来扩展语言语法。例如,您将拥有一个支持地理空间查询的 R 树。
So you write a public interface with the static constant:
所以你写了一个带有静态常量的公共接口:
public interface SyntaxExtensions {
// query type
String NEAR_TO_QUERY = "nearTo";
// params for query
String POINT = "coordinate";
String DISTANCE_KM = "distanceInKm";
}
Now later, a new developer thinks he needs to build a better index, so he comes and builds an R* implementation. By implementing this interface in his new tree he guarantees that the different indexes will have identical syntax in the query language. Moreover, if you later decided that "nearTo" was a confusing name, you could change it to "withinDistanceInKm", and know that the new syntax would be respected by all your index implementations.
现在后来,一个新的开发人员认为他需要构建一个更好的索引,所以他来构建一个 R* 实现。通过在他的新树中实现这个接口,他保证不同的索引在查询语言中具有相同的语法。此外,如果您后来认为“nearTo”是一个令人困惑的名称,您可以将其更改为“withinDistanceInKm”,并且知道您的所有索引实现都会遵守新语法。
PS: The inspiration for this example is drawn from the Neo4j spatial code.
PS:这个例子的灵感来源于Neo4j空间代码。