java 用于强制一致性的接口上静态方法的替代方案
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2806039/
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
Alternatives to static methods on interfaces for enforcing consistency
提问by jayshao
In Java, I'd like to be able to define marker interfaces, that forced implementations to provide static methods. For example, for simple text-serialization/deserialization I'd like to be able to define an interface that looked something like this:
在 Java 中,我希望能够定义标记接口,强制实现提供静态方法。例如,对于简单的文本序列化/反序列化,我希望能够定义一个如下所示的界面:
public interface TextTransformable<T>{
public static T fromText(String text);
public String toText();
Since interfaces in Java can't contain static methods though (as noted in a number of other posts/threads: here, here, and herethis code doesn't work.
由于 Java 中的接口不能包含静态方法(如许多其他帖子/线程中所述:here、here和here这段代码不起作用。
What I'm looking for however is some reasonable paradigm to express the same intent, namely symmetric methods, one of which is static, and enforced by the compiler. Right now the best we can come up with is some kind of static factory object or generic factory, neither of which is really satisfactory.
然而,我正在寻找的是一些合理的范式来表达相同的意图,即对称方法,其中之一是静态的,并由编译器强制执行。现在我们能想到的最好的是某种静态工厂对象或泛型工厂,这两种都不是很令人满意。
Note: in our case our primary use-case is we have many, many "value-object" types - enums, or other objects that have a limited number of values, typically carry no state beyond their value, and which we parse/de-parse thousands of time a second, so actually do care about reusing instances (like Float, Integer, etc.) and its impact on memory consumption/g.c.
注意:在我们的例子中,我们的主要用例是我们有很多很多“值对象”类型 - 枚举或其他具有有限数量值的对象,通常不携带超出其值的状态,并且我们解析/删除- 每秒解析数千次,所以实际上关心重用实例(如 Float、Integer 等)及其对内存消耗/gc 的影响
Any thoughts?
有什么想法吗?
EDIT1:To clear up some confusion - we have many, different objects fitting this pattern -really we're trying to come up with something elegant for callers with 2 semantics:
EDIT1:为了消除一些困惑——我们有很多不同的对象符合这种模式——我们真的试图为具有 2 个语义的调用者提供一些优雅的东西:
- Interfaces as contracts - unity of access (e.g. TextTransformable as a capability)
- Requiring implementation by subclasses/implementations (e.g. force them to implement their own transformation
- 作为契约的接口 - 访问的统一性(例如 TextTransformable 作为一种能力)
- 需要子类/实现的实现(例如强制它们实现自己的转换
In terms of our thoughts for Flyweight, Factories - they're both options we've considered, really we're trying to see if we can find something more elegant though than relying on JavaDoc saying "implement a Factory and delegate calls to it, or expose it at XXX location by convention"
就我们对 Flyweight、Factories 的想法而言——它们都是我们考虑过的选项,实际上我们试图看看我们是否能找到比依赖 JavaDoc 说“实现一个工厂并委托对它的调用”更优雅的东西,或按惯例将其暴露在 XXX 位置”
采纳答案by Yishai
This is really appropriate for the Flyweight. That is basically what you are trying to accomplish with the statics. In terms of how to serve the Flyweight object so that you don't create thousands of them, here are some ideas.
这对于蝇量级来说真的很合适。这基本上就是你试图用静力学来完成的。关于如何为享元对象提供服务,以便您不会创建数以千计的对象,这里有一些想法。
One is the factory, which you state you thought about and rejected, although you didn't state why (so any other ideas may suffer from the same problem) so I won't go into it.
一个是工厂,你说你考虑过并拒绝了,虽然你没有说明原因(所以任何其他想法可能会遇到同样的问题)所以我不会进入它。
Another is to have the value type have a method which can serve its converter. Something like this:
另一个是让值类型有一个可以为其转换器服务的方法。像这样的东西:
public class ValueType {
public static final TextTransformable<ValueType> CONVERT = ....
}
And then use it like this:
然后像这样使用它:
ValueType value = ValueType.CONVERT.fromText(text);
String text = ValueType.CONVERT.toText(value);
Now that doesn't enforce that all ValueType's provide their converters via the same mechanism, for that I think you need a factory of some kind.
现在这并不强制所有 ValueType 都通过相同的机制提供它们的转换器,为此我认为您需要某种工厂。
Edit: I guess I don't know what you find inelegant about a factory, but I think you are focused on callers, so how does this feel to you:
编辑:我想我不知道你觉得工厂有什么不雅的地方,但我认为你关注的是来电者,所以你感觉如何:
ValueType value = getTransformer(ValueType.class).fromText(text);
The above can be done with a static import of the factory and a method that has a signature like so:
以上可以通过工厂的静态导入和具有如下签名的方法来完成:
public static <T> TextTransformable<T> getTransformer(Class<T> type) {
...
}
The code to find the right transformer isn't necessarily the prettiest, but from the callers perspective everything is nicely presented.
找到正确转换器的代码不一定是最漂亮的,但从调用者的角度来看,一切都很好地呈现出来。
Edit 2: Thinking about this further, what I see is that you want to control object construction. You can't really do that. In other words, in Java you can't force an implementer to use or not use a factory to create their object. They can always expose a public constructor. I think your problem is that you aren't happy with the mechanisms for enforcing construction. If that understanding is right, then the following pattern may be of use.
编辑 2:进一步思考这个问题,我看到的是您想要控制对象构造。你真的不能那样做。换句话说,在 Java 中,您不能强制实现者使用或不使用工厂来创建他们的对象。他们总是可以公开一个公共构造函数。我认为您的问题是您对强制施工的机制不满意。如果这种理解是正确的,那么以下模式可能有用。
You create an object with only private constructors which wraps your value type. The object may have a generic type parameter to know what value type it wraps. This object is instantiated with a static factory method which takes a factory interface to create the "real" value object. All framework code that uses the object only takes this object as a parameter. It does not accept the value type directly, and that object cannot be instantiated without a factory for the value type.
您创建的对象仅包含包装您的值类型的私有构造函数。该对象可能有一个泛型类型参数,以了解它包装的值类型。该对象使用静态工厂方法实例化,该方法采用工厂接口来创建“真实”值对象。所有使用该对象的框架代码都只将该对象作为参数。它不直接接受值类型,并且该对象不能在没有值类型工厂的情况下实例化。
The problem with this approach is that it is quite restricting. There is only one way to create objects (those supported by the factory interface) and there is limited ability to use the value objects, as the code processing these text elements has limited interaction only through this object.
这种方法的问题在于它非常具有限制性。只有一种方法可以创建对象(工厂接口支持的对象)并且使用值对象的能力有限,因为处理这些文本元素的代码只能通过这个对象进行有限的交互。
I guess they say there isn't a software problem that can't be solved via an extra layer of indirection, but this may be a bridge too far. At least its food for thought.
我猜他们说没有一个软件问题不能通过额外的间接层来解决,但这可能是一个过分的桥梁。至少它值得深思。
回答by Eyal Schneider
Just an idea to consider: You can separate the transformation logic from the objects themselves, and then you have a fixed set of transformers, implementing the following interface :
只是一个需要考虑的想法:您可以将转换逻辑与对象本身分开,然后您有一组固定的转换器,实现以下接口:
public interface Transformer<T>{
public T fromText(String text);
public String toText(T obj);
}
Your actual data classes can have a method getTransformer() that returns the right transformer for them.
您的实际数据类可以有一个方法 getTransformer() 为它们返回正确的转换器。
回答by Sean Patrick Floyd
A totally different approach (and an ugly hack, for that matter) is to let the interface have a method that returns a method.
一个完全不同的方法(和丑陋的 hack,就此而言)是让接口有一个返回一个方法的方法。
public interface MyInterface{
Method getConvertMethod();
}
now your client code can do
现在你的客户端代码可以做
yourInterface.getConvertMethod().invoke(objectToBeConverted);
This is extremely powerful, but very bad API design
这是非常强大的,但是非常糟糕的 API 设计
Sean
肖恩
回答by Alex Byrth
Just a different idea. Not applicable for all cases, but maybe help someone else.
只是一个不同的想法。不适用于所有情况,但可能会帮助其他人。
You can use an abstract class, as bridge between your interfaceand your concrete classes. Abstract classes allows static methods as well contract methods definitions. As example, you can check out Collection API, where Sun implemented several abstract classes, instead of brute force coding from zero all concrete classes.
您可以使用abstract class, 作为您interface和您的concrete classes. 抽象类允许静态方法以及契约方法定义。例如,您可以查看 Collection API,其中 Sun 实现了几个抽象类,而不是从零开始对所有具体类进行暴力编码。
In some cases, you can just replace interfaces by abstract classes.
在某些情况下,您可以只用抽象类替换接口。
回答by aperkins
If you are running in Java 5 or higher, you can use the enum type - all of those are, by definition, singletons. So you could something like:
如果您在 Java 5 或更高版本中运行,则可以使用 enum 类型 - 根据定义,所有这些都是单例。所以你可以这样:
public enum MyEnumType {
Type1,
Type2,
//....
TypeN;
//you can do whatever you want down here
//including implementing interfaces that the enum conforms to.
}
This way the memory problem goes away, and you can have single instances of behavior implemented.
这样内存问题就消失了,您可以实现单个行为实例。
Edit:If you don't have access to enums (1.4 or earlier) or, for some other reason, they don't work for you, I would recommend a Flyweight patternimplementation.
编辑:如果您无权访问枚举(1.4 或更早版本),或者由于其他原因,它们对您不起作用,我建议您使用享元模式实现。
回答by Devon_C_Miller
As @aperkins said, you should use enums.
正如@aperkins 所说,您应该使用枚举。
The enum base class, Enum, provides a valueOf method that will convert a string to an instance.
枚举基类Enum提供了一个 valueOf 方法,可以将字符串转换为实例。
enum MyEnum { A, B, C; }
// Here's your toText
String strVal = MyEnum.A.getName();
// and here's your fromText
MyEnum value = MyEnum.valueOf(MyEnum.class, strVal);
Update:For the ones that are enums, this might do what you need. It uses reflection, so you only need to implement EnumHelper on enums where you have to deal with legacy values.
更新:对于枚举的那些,这可能会满足您的需求。它使用反射,因此您只需要在必须处理遗留值的枚举上实现 EnumHelper。
/** Enums can implement this to provide a method for resolving a string
* to a proper enum name.
*/
public interface EnumHelp
{
// Resolve s to a proper enum name that can be processed by Enum.valueOf
String resolve(String s);
}
/** EnumParser provides methods for resolving symbolic names to enum instances.
* If the enum in question implements EnumHelp, then the resolve method is
* called to resolve the token to a proper enum name before calling valueOf.
*/
public class EnumParser
{
public static <T extends Enum<T>> T fromText(Class<T> cl, String s) {
if (EnumHelp.class.isAssignableFrom(cl)) {
try {
Method resolve = cl.getMethod("resolve", String.class);
s = (String) resolve.invoke(null, s);
}
catch (NoSuchMethodException ex) {}
catch (SecurityException ex) {}
catch(IllegalAccessException ex) {}
catch(IllegalArgumentException ex) {}
catch(InvocationTargetException ex) {}
}
return T.valueOf(cl, s);
}
public <T extends Enum<T>> String toText(T value)
{
return value.name();
}
}
回答by les2
It seems like you need to separate the factory out from the created object.
似乎您需要将工厂与创建的对象分开。
public interface TextTransformer<T> {
public T fromText(String text);
public String toText(T source);
}
You can register TextTransformer classes however you like:
您可以根据自己的喜好注册 TextTransformer 类:
public class FooTextTransformer implements TextTransformer<Foo> {
public Foo fromText(String text) { return ...; }
public String toText(Foo source) { return ...; }
}
If you want FooTextTransformer to be a singleton, you can use a container like Spring to enforce that. Google has initiated a project to remove all manually enforced singletons from their codebase, but if you want to do an old-fashioned singleton, you could use a utility class:
如果您希望 FooTextTransformer 成为单例,则可以使用 Spring 之类的容器来强制执行。谷歌已经启动了一个项目,从他们的代码库中删除所有手动强制执行的单例,但如果你想做一个老式的单例,你可以使用一个实用程序类:
public class TextTransformers {
public static final FooTextTransformer FOO = new FooTextTransformer();
...
public static final BarTextTransformer BAR = new BarTextTransformer();
}
In your client code:
在您的客户端代码中:
Foo foo = TextTransformers.FOO.fromText(...);
...
foo.setSomething(...);
...
String text = TextTransformers.FOO.toText(foo);
This is just one approach off the top of my head.
这只是我头脑中的一种方法。

