Java – 强制子类具有静态方法的替代方法

我经常发现我想做这样的事情:

class Foo{ public static abstract String getParam(); } 

强制Foo的子类返回参数。

我知道你不能这样做,我知道为什么你不能这样做但是常见的选择:

 class Foo{ public abstract String getParam(); } 

是不满意的,因为如果你只是想知道参数的值并且实例化类是昂贵的,它需要你有一个没用的实例。

我非常有兴趣了解人们如何在不使用“恒定接口”反模式的情况下解决这个问题。

编辑:我将添加一些关于我的具体问题的更多细节,但这只是我想要做这样的事情的当前时间,过去还有其他几个。

我的子类都是数据处理器,超类定义它们之间的公共代码,它们允许它们获取数据,解析数据并将其放在需要的位置。 处理器每个都需要保存在SQL数据库中的某些参数。 每个处理器应该能够提供它所需的参数列表和默认值,以便通过检查每个处理器类型所需的参数来validation配置数据库或将其初始化为默认值。 在处理器的构造函数中执行它是不可接受的,因为它只需要每个类执行一次,而不是每个对象实例执行一次,并且应该在系统启动时完成,此时可能还不需要每种类型的实例。

您在静态上下文中可以做的最好的事情类似于以下之一:

一个。 有一个你专门寻找的方法,但不是任何合同的一部分(因此你不能强制执行任何人)并在运行时寻找它:

  public static String getParam() { ... }; try { Method m = clazz.getDeclaredMethod("getParam"); String param = (String) m.invoke(null); } catch (NoSuchMethodException e) { // handle this error } 

湾 使用注释,它会遇到同样的问题,因为你不能强迫人们把它放在他们的课堂上。

 @Target({TYPE}) @Retention(RUNTIME) public @interface Param { String value() default ""; } @Param("foo") public class MyClass { ... } public static String getParam(Class clazz) { if (clazz.isAnnotationPresent(Param.class)) { return clazz.getAnnotation(Param.class).value(); } else { // what to do if there is no annotation } } 

我同意 – 我觉得这是Java的限制。 当然,他们已经certificate了不允许inheritance静态方法的优点,所以我明白了,但事实是我遇到了这个有用的情况。 考虑这种情况:

我有一个父Condition类,并且对于它的每个子类,我想要一个getName()方法来声明类的名称。 子类的名称不是Java的类名,而是在Web前端用于JSON目的的一些小写文本字符串。 getName()方法不会更改每个实例,因此将其设置为静态是安全的。 但是,不允许Condition类的某些子类具有无参数构造函数 – 其中一些我需要在实例化时定义一些参数。

我使用Reflections库在运行时获取包中的所有类。 现在,我想要一个包含此包中每个Condition类的所有名称的列表,因此我可以将其返回到Web前端以进行JavaScript解析。 我将通过实例化每个类的努力,但正如我所说,他们并非都有无参数构造函数。 我设计了子类的构造函数,如果没有正确定义某些参数,则抛出IllegalArgumentException ,所以我不能仅传入null参数。 这就是为什么我希望getName()方法是静态的,但是对于所有子类都是必需的。

我目前的解决方法是执行以下操作:在Condition类(抽象)中,我定义了一个方法:

 public String getName () { throw new IllegalArugmentException ("Child class did not declare an overridden getName() method using a static getConditionName() method. This must be done in order for the class to be registerred with Condition.getAllConditions()"); } 

所以在每个子类中,我只是定义:

 @Override public String getName () { return getConditionName (); } 

然后我为每个定义一个静态getConditionName()方法。 这并不是“强迫”每个子类这样做,但我这样做的方式是,如果无意中调用了getName(),程序员会被指示如何解决问题。

在我看来,你想用错误的工具解决错误的问题。 如果所有子类都定义 (不能说真的inheritance)你的静态方法,你仍然无法轻松地调用它(在编译时调用一个未知的类的静态方法将通过reflection或字节代码操作)。

如果想要拥有一组行为,为什么不只使用所有实现相同接口的实例? 没有特定状态的实例在内存和构造时间方面是便宜的,如果没有状态,您总是可以为所有调用者共享一个实例(flyweight模式)。

如果您只需要将元数据与类耦合,您可以构建/使用您喜欢的任何元数据工具,最基本的(手工)实现是使用类对象是键的Map。 如果这适合您的问题取决于您的问题,您没有详细描述。

编辑:(结构)元数据将数据与关联(这只是一种风格,但可能是更常见的风格)。 注释可以用作非常简单的元数据工具(使用参数注释类)。 有无数其他方法(和目标要实现),复杂的一面是框架,它基本上提供了设计到UML模型中的每一点信息,以便在运行时进行访问。

但是你所描述的(数据库中的处理器和参数)就是我所说的“行为集”。 并且“ 每个类需要加载一次参数”的论点没有实际意义,它完全忽略了可以用来解决这个问题而不需要任何“静态”的习语。 即,flyweight模式(仅用于一次实例)和延迟初始化(仅用于工作一次)。 根据需要与工厂结合。

我一遍又一遍地遇到同样的问题,我很难理解为什么Java 8更喜欢实现lambda而不是那个。

无论如何,如果您的子类只实现检索一些参数并执行相当简单的任务,您可以使用枚举,因为它们在Java中非常强大:您基本上可以将它视为一组固定的接口实例。 他们可以拥有成员,方法等。他们只是不能实现(因为他们是“预先实例化”)。

 public enum Processor { PROC_IMAGE { @Override public String getParam() { return "image"; } }, PROC_TEXT { @Override public String getParam() { return "text"; } } ; public abstract String getParam(); public boolean doProcessing() { System.out.println(getParam()); } } 

好处是你可以通过调用Processor.values()获得所有“实例”:

 for (Processor p : Processorvalues()) { System.out.println(String.format("Param %s: %s", p.name(), p.getParam())); p.doProcessing(); } 

如果处理更复杂,您可以在枚举方法中实例化的其他类中执行此操作:

 @Override public String getParam() { return new LookForParam("text").getParam(); } 

然后,您可以使用您能想到的任何新处理器来丰富枚举。

缺点是,如果其他人想要创建新的处理器,则无法使用它,因为这意味着修改源文件。