Java抽象静态变通方法

时间:2009-12-16 16:51:24

标签: java inheritance interface

据我所知,由于模糊问题,抽象类和接口都不能包含abstract and static的方法,但有没有解决方法?

我想要一个抽象类或一个接口,要求在扩展/实现这个类/接口的所有类中包含一个静态方法。有没有办法在Java中这样做?如果没有,这可能是我对Java的最后一根稻草......

编辑1:这个问题的背景是我有一堆类,现在称它们为Stick,Ball和Toy,它们在数据库中有一堆条目。我想创建一个名为Fetchable的超类/接口,它需要在它下面的每个类中使用静态方法getFetchables()。 Stick,Ball和Toy中的方法必须是静态的原因是因为它们将与数据库通信以检索数据库中每个类的所有条目。

编辑2:对于那些说你不能用任何语言做到这一点的人,那是不对的。你当然可以在Ruby中继承类方法。这不是某人没有获得OO的情况,这是Java语言中缺少功能的情况。你可以试着争辩说你永远不需要继承静态(类)方法,但这是完全错误的,我会忽略任何能产生这些观点的答案。

15 个答案:

答案 0 :(得分:7)

您有几个选择:

  1. 使用反射查看方法是否存在然后调用它。
  2. 为名为@GetAllWidgetsMethod的静态方法创建注释。

  3. 正如其他人所说,尽量不要使用静态方法。

答案 1 :(得分:6)

有很多答案关于'这没有意义......'但事实上我昨天遇到了类似的问题。

我想在单元测试中使用继承。我有一个API和几个实现。所以我只需要对所有实现进行一组单元测试,但使用不同的setUp方法是静态的。

解决方法:所有测试都是抽象类,其中一些静态字段带有受保护的访问修饰符。在所有实现中,我添加了静态方法来设置这些静态字段。它工作得相当不错,我避免了复制和粘贴。

答案 2 :(得分:4)

我也在处理这个问题。对于那些坚持认为“没有意义”的人,我会邀请你在那个语义框之外思考一下。我正在与之合作的计划本质上就是反思。

如你所知,反射比直接二进制函数调用要长三个数量级。这是一个不可避免的问题,软件需要移植到尽可能多的机器,其中一些机器将比我的开发机器开始时的32位和慢。因此,需要通过静态方法检查类对所请求操作的适用性,并且在模块引导期间立即运行所有反射方法。

一切都是有效的,首要的是。我已经构建了整个东西。唯一的问题是模块可以在.class中编译而无需编译时检查是否存在标识静态函数,从而导致一个天生无用的类。没有标识符及其包含的信息,为安全起见,模块未加载。

我清楚地理解“抽象”和“静态”的完整定义的问题,并且理解它们没有合理意义。但是,Java中缺少编译器强制包含的类方法的能力,而且我喜欢这种语言,我很想念它。因此,对于每个曾经使用该软件的程序员来说,这是一个人为限制因素,我相信我们都同意这是一种痛苦。

答案 3 :(得分:2)

静态方法与对象的整个相关,而不是与单个实例相关。允许覆盖静态方法会打破这个格言。

我要考虑的第一件事是从非静态上下文访问您的数据库。这实际上是Java应用程序的标准。

如果绝对必须使用静态方法,则使用实例特定参数(泛型类型)对其进行参数化,以允许不同的子类与之交互。然后从多态方法中调用单个静态方法。

答案 4 :(得分:1)

  

有没有办法在Java中执行此操作?

我认为没有办法用任何语言来做到这一点。没有意义,因为静态方法属于一个类,不能多态调用。启用多态调用是接口和抽象类存在的唯一原因。

答案 5 :(得分:1)

类型系统允许您在类型之间表达一些约束,但它是有限的。这就是为什么javadocs充斥着人类语言的限制,要求人们遵循编译器无法检查的规则。

如果您想将其扩展到本机提供的语言之外,您可以编写自己的静态分析工具。这并不罕见。例如:findbug。 IDE也会这样做,他们检查超出语言规定的东西。您可以编写一个插件来强制子类必须具有此类签名的静态方法。

在你的情况下,它是不值得的。让超类中的javadoc敦促实现者包含一个静态方法,这已经足够了。

无论如何,我会提供一种表达你的约束的复杂方式,但不要做。人们真的被带走了在编译时让一切都可以检查,代价是代码不可读。

interface WidgetEnumerator
{
    List getAllWidgets();
}

public class Abs<T extends WidgetEnumerator>
{
    static List getAllWidgets(Class<? extends Abs> clazz){ ... }
}

public class Sub extends Abs<SubWidgetEnumerator>
{
}

public class SubWidgetEnumerator implements WidgetEnumerator
{
    public List getAllWidgets() { ... }
}

工作原理:对于Abs的任何子类,它被强制提供WidgetEnumerator的实现。子类作者不能忘记这一点。现在,调用Abs.getAllWidgets(Sub.class)包含足以解决该实现的信息,即SubWidgetEnumerator。它是通过反射完成的,但它是类型安全的,不涉及字符串文字。

答案 6 :(得分:1)

我认为在看到您的编辑后我可以给您更好的答案 - 您最好的选择可能是工厂模式。 (不可爱,但比单身人士好。)

abstract class Widget
    public static Widget[] getAllWidgetsOfType(Class widgetType) {
        if(widgetType instanceof ...)
    }
class Ball extends Widget
class Stick extends Widget
class Toy extends Widget

这不是一个很好的方法,但这是典型的。 Hibernate是您通常用来解决此问题的工具,这正是它的设计目标。

最大的问题是,只要添加给定类型的新类,就需要编辑基类。没有反思就无法解决这个问题。如果你想使用反射,那么你可以用这种方式实现它(Psuedocode,我不会查找反射的确切语法,但它并不比这复杂得多):

public static Widget[] getAllWidgetsOfType(Class widgetType) {
    Method staticMethod=widgetType.getStaticMethod("getAllInstances");
    return staticMethod.invoke();
}

这将提供您所要求的解决方案(每次添加子类时都需要修改基类是一种很好的直觉)。

您也可以将其设为实例方法而不是静态方法。这没有必要,但你可以在Widget中对方法(抽象)进行原型设计。

同样,与Hibernate相比,所有这些都是不必要的和草率的......

编辑:如果您传入一个实时的“空”球,棒或玩具实例而不是它的“类”对象,那么您可以调用一个继承的方法而根本不使用反射。这也可以,但您必须扩展Widget的定义以包含用作键的“Empty”实例。

答案 7 :(得分:1)

有很多“这没有意义”或“这不可能是因为”和“为什么要这样?” (或更糟糕的是:“您不需要它!”)在所有这些答案中。但是,这些答案也间接地说明了为什么有可能这样做的原因。

必须在概念和实现之间进行区分。 当然,覆盖静态方法没有任何意义。而且这也不是问题所在。

有人要求一种在抽象类的每个派生类中强制执行某种静态方法(或常量或其他方法)的方法。为什么要这样做,是要与Jave撰写书名而不与他人打交道的人的事。

这与编译器如何编译方法以及如何在运行时完成方法无关。

为什么有可能?因为有些东西是特定于类的(而不是特定于实例的),因此应该是静态的,而它们需要在每个子类(或实现接口的类)中实现。

假设有一个抽象类'Being'。现在有子类,例如“动物”和“植物”。 现在只允许哺乳动物和鱼类作为动物。此信息特定于动物类,不属于任何实例,也不属于任何超类或子类。但是,此信息必须由类而不是实例提供,因为正确构造动物实例是必需的。因此它必须存在,并且不能在实例中。

实际上,Java有这样的事情-每个对象都有一个特定于类的字段'class'。它是特定于类的,不能继承,没有覆盖,并且必须存在。编译器可以隐式创建它,但是显然编译器可以做到。那么,为什么不也允许自己的字段呢? 毕竟,当编译器检查initritit链中的抽象函数时,如何解释“抽象静态”组合只是一个定义问题。 从来没有人要求过应该继承超类的类函数(根据该函数的实际作用,这仍然是有意义的-在所有类都继承了其超类的静态函数之后,即使您可能会得到警告,当您通过子类调用它时应直接访问它))

但是要总结一下:Java语言没有提供在编译时执行此操作的方法,而没有理由(没有普通的教条主义)这样做。 唯一的方法是将静态最终函数写入抽象类,该抽象类在加载子类时尝试查找子类的静态函数/字段(或加载所有现有子类并对其进行检查)。如果制作正确,它将在首次使用时出现运行时错误。复杂而肮脏,但总比没有好。至少,它可以防止从错误的超类获取信息的错误。 但是,它不适用于接口。

答案 8 :(得分:0)

没有。你不能这样做。如果您愿意妥协并使方法非静态或在抽象类中提供静态方法的实现,那么您将能够用Java编写它。

答案 9 :(得分:0)

创建一个上下文界面,其中包含您的方法,其名称与您的问题域相匹配。 (如果你绝对需要,请将其命名为“World”,但大部分时间都有更好的名称)

传递上下文对象的实现实例。

答案 10 :(得分:0)

静态方法不能是抽象的,因为它们不是虚拟的。因此,调用它们的任何地方都必须具有实现的具体类型。如果要强制接口的所有实现都具有某种静态方法,则表明需要进行单元测试。

abstract class A
{
    public static void foo()
    {
        java.lang.System.out.println("A::foo");
    }

    public void bar()
    {

        java.lang.System.out.println("A::bar");
    }
}

class B extends A
{
    public static void foo()
    {
        java.lang.System.out.println("B::foo");
    }

    public void bar()
    {

        java.lang.System.out.println("B::bar");
    }
}

public class Main
{
    public static void main(String[] args)
    {
        B b = new B();
        b.foo();
        b.bar();

        A a = b;
        a.foo();
        a.bar();
    }
}

答案 11 :(得分:0)

为了什么值得,我确切地知道你要做什么。

我在搜索我无法做到的原因时发现了这篇文章。

在我的情况下,我拥有从中央基础继承的HUNDREDS类,我只想得到这样的引用:

ValueImSearchingFor visf = StaticClass.someArbitraryValue()

我不想为数百个继承类中的每一个编写/维护someArbitraryValue() - 我只想编写一次逻辑并让它为每个未来编写一个唯一的Class-Sepcific值class没有触及基类。

是的,我完全得到OO - 只要它已经可用,我就一直在写Java。

这些特定类更像是&#34;定义&#34;而不是实际的对象,每次我只需要查看someArbitraryValue()实际上是什么时,我都不想实例化。

将其视为PUBLIC STATIC FINAL,允许您运行Method ONCE以初始设置它。 (实际上,当你定义一个Enum时,你可以做到这一点......)

答案 12 :(得分:-1)

好吧,也许我的问题很难被问到,似乎大多数人都没有得到我想做的事情。尽管如此,我有一个令人满意的解决方案。

在抽象超类中,我将得到一个静态方法getAllWidgets(Class type)。在其中,我将检查您传递它的类,并根据它进行正确的提取。一般来说,我喜欢避免传递类和在这样的东西上使用开关,但我会在这里做一个例外。

答案 13 :(得分:-1)

我用一个抽象的Widget内部类创建一个WidgetCollection类。

您可以为每种类型的Widget扩展WidgetCollection.Widget类。

不需要静态方法。

示例(未编译或测试):

class WidgetCollection<W extends Widget> {
    Set<W> widgets = new HashSet<W>();

    Set<W> getAll() {
        return widgets;
    }

    abstract class Widget {

       Widget() {
           widgets.add(this);
       }

       abstract String getName();
    }

    public static void main(String[] args) {
         WidgetCollection<AWidget> aWidgets = new WidgetCollection<AWidget>();
         a.new AWidget();

         Set<AWidget> widgets = aWidgets.getAll();
    }
}

class AWidget extends Widget {
    String getName() {
        return "AWidget";
    }
}

答案 14 :(得分:-2)

做你要问的事是没有意义的:

Why can't static methods be abstract in Java