2012-08-13 46 views
6

我经常发现我想要做这样的事情:爪哇 - 替代迫使子类有一个静态方法

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

要强制美孚的一个子类返回的参数。

我知道你不能做到这一点,我知道你为什么不能做到这一点,但共同的选择:

class Foo{ 
public abstract String getParam(); 
} 

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

我很想知道人们如何避免使用“恒定接口”反模式。

编辑:我会添加一些更详细的关于我的具体问题,但是这仅仅是当我想要做这样的事情有几个人从过去的当前时间。

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

+3

如果您已经调用了'Class.getName()',那么为什么还需要获取名称? – 2012-08-13 16:34:17

+3

这是一个例子。我可以说得到gigawidgets – 2012-08-13 16:39:07

+0

有趣的问题。你有一个方便的具体例子吗?这可能有助于挑起一些想法。斯卡拉有一个很好的方式来处理这个问题 - 它是一个对象和一个类的区别 - 可能不会帮助你很多。 – jeff 2012-08-13 17:05:17

回答

4

在我看来,你想用错误的工具解决错误的问题。 如果定义(真的不能说,继承)的静态方法,你仍然无法无痛调用它所有的子类(要调用在编译时不知道一个类中的静态方法是通过反射或字节码操作)。

如果想法是有一组行为,为什么不使用全部实现相同接口的实例?没有特定状态的实例在内存和构建时间方面便宜,如果没有状态,则可以为所有呼叫者共享一个实例(flyweight模式)。

如果你只需要使用类,你可以建立/使用任何的元数据功能,你喜欢的,最基本的(手)的实现是使用一个Map,其中类对象是关键情侣元。如果这适合你的问题取决于你的问题,你没有详细描述。

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

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

+0

你能否更详细地描述你的最后一段。我已经为这个问题添加了更多细节。我想避免集中保存元数据, – 2012-08-14 09:03:08

+0

@NickLong您似乎已经坚定了'静态'方法。你需要调整你的心态。拥有一个类的实例打开了继承和多态的所有优点。认为这样一个funtcion持有者实例被浪费 - 每个实例〜16个字节不会让你的应用成为一个吃巨兽的内存。 – Durandal 2012-08-14 13:14:55

+0

您的数据处理器+参数*示例数据处理器*工厂*会对已初始化的实例进行处理。另外请注意,*静态*比*更集中*(后者至少可以在运行时在需要时更改)。静态意味着自动*编译时间依赖性*(为了避免你*有*动态变化,至少部分原因)。 – Durandal 2012-08-14 13:18:38

8

,你可以在这里做一个静态的情况下,最佳是类似以下内容之一:

一个。你有没有专门寻找一种方法,但没有任何合同的一部分(因此你不能强制任何人来实现),并寻求在运行时:

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 
    } 
} 
+0

注释的好处。 – Matthieu 2016-09-11 21:20:49

4

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

我有一个父类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()被无意中调用,程序员将被指示如何解决问题。

1

我一次又一次地遇到同样的问题,我很难理解为什么Java 8更愿意实现lambda而不是那个。无论如何,如果你的子类只实现检索一些参数并执行相当简单的任务,你可以使用enumerations,因为它们在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(); 
} 

如果处理比较复杂,你可以在在enum实例化其他类做方法:

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

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

不好的一面是,如果其他人想要创建新的处理器,就不能使用它,因为这意味着修改源文件。