Java:没有 ‘abstract static’ 的解决方法

huangapple go评论77阅读模式
英文:

Java: workaround for no 'abstract static'

问题

我正在编写一个关于扭曲谜题的应用程序:魔方、金字塔魔方、斜转魔方等等。
我有一个抽象类'扭曲谜题',以及派生类:扭曲魔方、扭曲金字塔魔方等等。
我有一个对话框,提供关于每个谜题的基本信息:名称、发明者、发明年份,以及谜题如何工作的基本描述。

我认为每个谜题的这些信息最好保存在每个'实现'类中,如TwistyCube、TwistyPyraminx等。静态方法强制执行自身:

class TwistyCube extends TwistyPuzzle

static String getName()
  {
  return "Rubik Cube";
  }  

但似乎没有办法让需要显示此信息的对话框查询这些类 - 因为在Java中没有'抽象静态'方法的概念。

那么我的选择是什么?在Java中,如何标准地解决缺少'抽象静态'的问题?

英文:

I am writing an app about Twisty Puzzles: the Rubik Cube, Pyraminx, Skewb and the like.
I have an abstract class 'TwistyPuzzle' and derived classes: TwistyCube, TwistyPyraminx, etc.
I have a dialog which provides basic information about each puzzle: it's name, its inventor, year of invention, basic description how the puzzle works.

This information about each puzzle is - IMHO - best kept inside each of the 'implementation' classes TwistyCube, TwistyPyraminx, etc. Static methods force themselves:

class TwistyCube extends TwistyPuzzle

static String getName()
  {
  return "Rubik Cube";
  }  

but there seems to be no way to have the Dialog, which needs to display this information, query the classes - because there's no concept of an 'abstract static' method in Java.

So what are my options? What is the standard workaround for the lack of 'abstract static' in Java?

答案1

得分: 2

正如你所指出的,你不能声明一个abstract static方法(或字段)。

这不仅仅是语法问题。根本原因在于Java的static成员不会被继承。这意味着static成员通常不能以多态的方式使用。当你调用一个static方法或访问一个static字段时,调用/使用哪个类的成员在编译时确定,仅基于编译时的类型考虑。

所以...基本上...你想要做的这件事是无法实现的。

但是有一个解决方案。如果你想在一个方法上使用abstract,将它声明为实例方法。无论你是否这样做是为了可以以多态方式使用该方法...或者出于其他原因。

你通过以下论点反对这个观点:

> 像那样的方法应该是非静态的,因为你总是会在一个实例上调用它。 - Thomas 2小时前

> 当然不是了 - 所有'扭曲魔方'TwistyRubik'的实例的名称都是相同的。显然它属于类,而不属于它的实例。 - Ralf Kleberhoff 1小时前

但是我认为你忽略了要点。方法并不是属于实例的。方法属于类。即使是非静态方法。我们将非静态方法称为“实例”方法,因为它们通常在特定实例的状态上操作。但它们不必如此。实例方法可以返回有关其所属类的信息。

以下是我如何编写你的示例:

public abstract class TwistyPuzzle {
    public abstract String getName();
    ...
}

public class TwistyCube extends TwistyPuzzle {
    public String getName() {
        return "Rubik Cube";
    }
    ...
}

那么让我们分析一下:

  • 问:它是否具有所需的行为? 答:是的。每个TwistyCube实例将返回"Rubik Cube"

  • 问:将其设置为非静态方法是否有任何空间开销? 答:没有。方法代码是共享的,由于Java不将方法视为实例的可更新属性,因此没有每个对象的存储开销。

  • 问:将其设置为非静态方法是否会有性能开销? 答:在大多数情况下,不会。如果存在开销,那么它是最小的。

  • 问:与static方法相比,是否有优势? 答:是的。getName()可以在具有运行时方法调度的情况下以多态方式使用。

那么实际的问题是什么? "属于"是什么意思?

坦率地说,我认为这只是关于你对实例方法的概念化方式。调用实例上的方法并不意味着它必须返回特定于该实例的信息。

将Java视为一种工具。按照它设计的方式使用它。如果你想要提供有关当前Java类的信息的abstract(例如多态的)方法,那么就是标准的方法。

后续

> 那用一个单独的枚举如何 - 每种类型的'扭曲魔方'TwistyPuzzle'对应一个值 - 来提供这种信息呢?

我猜那也可以。但现在你有了一个问题,就是如何获取与你查询的特定TwistyPuzzle实例(或类)相关联的正确enum值。并且对于与每个TwistyPuzzle类相关的信息,存在着一个可读性/可维护性问题;即enum

英文:

As you noted, you cannot declare an abstract static method (or field).

This is not just syntax. The fundamental reason is that Java static members are not inherited. And that means that static members cannot normally be used polymorphically. When you call a static method or access a static field, the decision of which classes member is called / used is determined at compile time, based solely on compile time type considerations.

So ... basically ... what you are trying to do cannot be done.

But there is a solution. If you want to use abstract on a method, declare it as an instance method. Whether you are doing this so that you can use the method polymorphically ... or for other reasons.

You argued against this by saying:

> A method like that should be non-static because you'll always call it on an instance. – Thomas 2 hours ago

> Of course not - the name of all instances of 'TwistyRubik' is the same. It clearly belongs to the class, not its instances. – Ralf Kleberhoff 1 hour ago

But I think you are missing the point. Methods don't "belong" to instances. Methods belongs to classes. Even non-static methods. We call non-static methods "instance" methods because they typically operate on the state of a specific instance. But they don't need to. And instance method can return information about the class it belongs to.

Here's how I would write your example:

public abstract class TwistyPuzzle {
    public abstract String getName();
    ...
}
    
public class TwistyCube extends TwistyPuzzle
    public String getName();
        return "Rubik Cube";
    }
    ...
}

So lets analyze this:

  • Q: Does it have the desired behavior? <br>A: Yes. Every TwistyCube instance will return &quot;Rubik Cube&quot;.

  • Q: Is there any space overhead of making this a non-static method? <br>A: No. Method code is shared, and since Java doesn't treat methods as updatable properties of an instance, there is no per-object storage overhead.

  • Q: Is there an performance overhead in making this a non-static method? <br>A: In most cases No. And if there is an overhead, then it is minimal.

  • Q: Are there advantages over a static method? <br>A: Yes. The getName() can be used polymorphically with runtime method dispatching.

So what is the actual problem here? What does "belong" mean?

Frankly, I think it is just about the way that you have conceptualized instance methods. The fact that you called a method on an instance doesn't mean that it needs to return information specific to that instance.

Think of Java as a tool. Use it the way it was designed to be used. If you want abstract (e.g. polymorphic) methods that deliver information about the current Java class, then this is the standard way to do it.


FOLLOWUP

> How about a separate enum - one value per one type of a TwistyPuzzle - that would provide such info?

I guess that would work. But now you have the problem of getting the correct enum value that relates to a specific TwistyPuzzle instance (or class) that you are enquiring about. And you have a readability / maintainability issue with information pertaining to each TwistyPuzzle class living in a separate class; i.e. the enum.

huangapple
  • 本文由 发表于 2020年10月23日 20:39:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/64500173.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定