Java抽象静态解决方法
我知道,由于模糊问题,抽象类和接口都不能包含抽象和静态的方法,但有没有解决方法?
我希望有一个抽象类或一个接口,要求在扩展/实现此类/接口的所有类中包含静态方法。 有没有办法在Java中这样做? 如果没有,这可能是我与Java的最后一根稻草……
编辑1:这个问题的背景是我有一堆类,现在称它们为Stick,Ball和Toy,它们在数据库中有一堆条目。 我想创建一个名为Fetchable的超类/接口,它需要在它下面的每个类中使用静态方法getFetchables()
。 Stick,Ball和Toy中的方法必须是静态的原因是因为它们将与数据库通信以检索数据库中每个类的所有条目。
编辑2:对于那些说你不能用任何语言做到这一点的人,那是不正确的。 你当然可以在Ruby中inheritance类方法。 这不是某人没有获得OO的情况,这是Java语言中缺少function的情况。 您可以尝试争辩说您永远不需要inheritance静态(类)方法,但这是完全错误的,我将忽略任何提出这些要点的答案。
你有几个选择:
- 使用reflection来查看方法是否存在然后调用它。
-
为名为@GetAllWidgetsMethod的静态方法创建注释。
-
正如其他人所说,尽量不要使用静态方法。
关于“这没有意义……”有很多答案,但事实上我昨天遇到了类似的问题。
我想在unit testing中使用inheritance。 我有一个API和几个它的实现。 所以我只需要对所有实现进行一组unit testing,但使用不同的setUp方法是静态的。
解决方法:所有测试都是抽象类,其中一些静态字段具有受保护的访问修饰符。 在所有实现中,我添加了静态方法来设置这些静态字段。 它工作得相当不错,我避免了复制和粘贴。
我也在处理这个问题。 对于那些坚持认为“没有意义”的人,我会邀请你在那个语义框之外思考一下。 我正在处理的程序本质上是关于反思的。
如你所知,reflection比直接二进制函数调用要长三个数量级。 这是一个不可避免的问题,软件需要移植到尽可能多的机器,其中一些机器将比我的开发机器开始时的32位和慢。 因此,需要通过静态方法检查类对所请求操作的适用性,并且在模块引导期间立即运行所有reflection方法。
一切都是有效的,首先是最重要的。 我已经构建了整个事物。 唯一的问题是模块可以在.class中编译而无需编译时检查是否存在标识静态函数,从而导致一个天生无用的类。 没有标识符及其包含的信息,为安全起见,模块未加载。
我清楚地理解了“抽象”和“静态”的完整定义的问题,并且理解它们在一起没有意义。 但是,Java中缺乏编译器强制包含的类方法的能力,而且我喜欢这种语言,我很想念它。 因此,对于每个曾经在软件上工作的程序员来说,这是一个人为限制,我相信我们都同意这是一个痛苦。
静态方法与整个对象类相关,而不是与单个实例相关。 允许覆盖静态方法会打破这个格言。
我要考虑的第一件事是从非静态上下文访问您的数据库。 这实际上是Java应用程序的标准。
如果绝对必须使用静态方法,则使用实例特定参数(generics类型)对其进行参数化,以允许不同的子类与之交互。 然后从您的多态方法中调用该单个静态方法。
有没有办法在Java中这样做?
我认为没有办法用任何语言来做到这一点。 没有意义,因为静态方法属于一个类,不能多态调用。 启用多态调用是接口和抽象类存在的唯一原因。
类型系统允许您在类型之间表达一些约束,但它是有限的。 这就是为什么javadocs充斥着人类语言的限制,要求人们遵循编译器无法检查的规则。
如果你想将它扩展到本机提供的语言之外,你可以编写自己的静态分析工具。 这并不罕见。 例如:findbug。 IDE也可以这样做,他们检查超出语言规定的东西。 您可以编写一个插件来强制子类必须具有此类签名的静态方法。
在你的情况下,它是不值得的。 让超类中的javadoc敦促实现者包含一个静态方法,这已经足够了。
无论如何,我会提供一种表达你的约束的复杂方式,但不要做。 人们真的被带走了在编译时让一切都可以检查,代价是代码不可读。
interface WidgetEnumerator { List getAllWidgets(); } public class Abs { static List getAllWidgets(Class extends Abs> clazz){ ... } } public class Sub extends Abs { } public class SubWidgetEnumerator implements WidgetEnumerator { public List getAllWidgets() { ... } }
工作原理:对于Abs
任何子类,它被迫提供WidgetEnumerator的实现。 子类作者不能忘记这一点。 现在调用Abs.getAllWidgets(Sub.class)
包含足够的信息来解析该实现,即SubWidgetEnumerator
。 它是通过reflection完成的,但它是类型安全的,不涉及字符串文字。
我想在看到你的编辑后我可以给你一个更好的答案 – 你最好的选择可能是工厂模式。 (不可爱,但比单身人士好)。
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是您通常用来解决此问题的工具,这正是它的设计目标。
最大的问题是,只要添加给定类型的新类,它就需要编辑基类。 没有反思就无法解决这个问题。 如果你想使用reflection,那么你可以用这种方式实现它(Psuedocode,我不会查找reflection的确切语法,但它并不比这复杂得多):
public static Widget[] getAllWidgetsOfType(Class widgetType) { Method staticMethod=widgetType.getStaticMethod("getAllInstances"); return staticMethod.invoke(); }
这将提供您所要求的解决方案(每次添加子类时都需要修改基类是一种很好的直觉)。
您还可以将其设为实例方法而不是静态方法。 这没有必要,但你可以在Widget中对方法(抽象)进行原型设计。
同样,与Hibernate相比,所有这些都是不必要的和草率的……
编辑:如果您传入一个实时的“空”球,棒或玩具实例而不是它的“类”对象,那么您可以只调用一个inheritance的方法而不使用reflection。 这也可以,但您必须扩展Widget的定义以包含用作键的“Empty”实例。
不,你不能这样做。 如果您愿意妥协并使方法非静态或在抽象类中提供静态方法的实现,那么您将能够使用Java进行编码。
创建一个上下文界面,其中包含您的方法,其名称与您的问题域相匹配。 (如果你绝对必须把它命名为“世界”,但大部分时间都有一个更好的名字)
传递上下文对象的实现实例。
静态方法不能是抽象的,因为它们不是虚拟的。 因此,调用它们的任何地方都必须具有实现的具体类型。 如果要强制接口的所有实现都具有某种静态方法,那么这表明需要进行unit testing。
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(); } }
对于它的价值,我确切地知道你要做什么。
我找到这篇文章的同时也在寻找我无法做到的原因。
在我的情况下,我拥有从中央基础inheritance的HUNDREDS类,我只想获得这样的引用:
ValueImSearchingFor visf = StaticClass.someArbitraryValue()
我不想为数百个inheritance类中的每一个编写/维护someArbitraryValue() – 我只想编写一次逻辑并让它为每个未来的编写类计算一个Unique Class-Sepcific值而不触及基类。
是的,我完全得到OO – 只要它可用,我就一直在写Java。
这些特定的类更像是“定义”而不是实际的对象,我不想每次只需要查看someArbitraryValue()实际上是什么就实例化。
可以将它想象为PUBLIC STATIC FINAL,它允许您运行Method ONCE来初始设置它。 (有点像你实际定义一个枚举时可以做的……)
好吧,也许我的问题很难被问到,似乎大多数人都没有得到我想做的事情。 尽管如此,我有一个令人满意的解决方案。
在抽象超类中,我将有一个静态方法getAllWidgets(类类型)。 在其中,我将检查您传递它的类,并根据它进行正确的提取。 一般来说,我喜欢避免传递类和在这样的东西上使用开关,但我会在这里做一个例外。
我将使用抽象的Widget内部类创建一个WidgetCollection类。
您可以为每种类型的Widget扩展WidgetCollection.Widget类。
没有必要的静态方法。
示例(未编译或测试):
class WidgetCollection { Set widgets = new HashSet (); Set getAll() { return widgets; } abstract class Widget { Widget() { widgets.add(this); } abstract String getName(); } public static void main(String[] args) { WidgetCollection aWidgets = new WidgetCollection (); a.new AWidget(); Set widgets = aWidgets.getAll(); } } class AWidget extends Widget { String getName() { return "AWidget"; } }
做你要问的事是没有意义的:
为什么静态方法在Java中不能是抽象的