如何在 Java 中实现抽象静态方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22458838/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:50:41  来源:igfitidea点击:

How can I implement abstract static methods in Java?

javaoopinterfacestaticabstract

提问by John Gowers

There are numerous questions about the impossibility of including static abstract Java methods. There are also quite a lot about workarounds for this (design flaw/design strength). But I can't find any for the specific problem I'm going to state shortly.

关于不可能包含静态抽象 Java 方法的问题有很多。对此也有很多解决方法(设计缺陷/设计强度)。但是我找不到任何有关我将要说明的特定问题的信息。

It seems to me that the people who made Java, and quite a lot of the people who use it, don't think of static methods the way I, and many others, do - as class functions, or methods that belong to the class and not to any object. So is there some other way of implementing a class function?

在我看来,Java 的开发者,以及很多使用它的人,并不像我和其他许多人那样认为静态方法是类函数,或者属于类的方法而不是任何对象。那么还有其他实现类函数的方法吗?

Here is my example: in mathematics, a groupis a set of objects that can be composed with each other using some operation * in some sensible way - for example, the positive real numbers form a group under normal multiplication (x* y= x× y), and the set of integers form a group, where the 'multiplication' operation is is addition (m* n= m+ n).

这是我的例子:在数学中,是一组对象,可以使用某种操作*以某种合理的方式相互组合 - 例如,正实数在正常乘法下形成一个组(x* y= x× y),并且整数集形成一个组,其中“乘法”运算是加法(m* n= m+ n)。

A natural way to model this in Java is to define an interface (or an abstract class) for groups:

在 Java 中对此进行建模的一种自然方法是为组定义一个接口(或抽象类):

public interface GroupElement
{
  /**
  /* Composes with a new group element.
  /* @param elementToComposeWith - the new group element to compose with.
  /* @return The composition of the two elements.
   */
  public GroupElement compose(GroupElement elementToComposeWith)
}

We can implement this interface for the two examples I gave above:

我们可以为我上面给出的两个例子实现这个接口:

public class PosReal implements GroupElement
{
  private double value;

  // getter and setter for this field

  public PosReal(double value)
  {
    setValue(value);
  }

  @Override
  public PosReal compose(PosReal multiplier)
  {
    return new PosReal(value * multiplier.getValue());
  }
}

and

public class GInteger implements GroupElement
{
  private int value;

  // getter and setter for this field

  public GInteger(double value)
  {
    setValue(value);
  }

  @Override
  public GInteger compose(GInteger addend)
  {
    return new GInteger(value + addend.getValue());
  }
}

However, there's one other important property that a group has: every group has an identity element- an element esuch that x* e= xfor all xin the group. For example, the identity element for positive reals under multiplication is 1, and the identity element for integers under addition is 0. In that case, it makes sense to have a method for each implementing class like the following:

然而,组还有另一个重要的属性:每个组都有一个单位元素——一个元素e,使得x* e= x对于组中的所有x。例如,乘法下正实数的单位元是1,加法下整数的单位元是0。在这种情况下,为每个实现类设置一个方法是有意义的,如下所示:

public PosReal getIdentity()
{
  return new PosReal(1);
}

public GInteger getIdentity()
{
  return new GInteger(0);
}

But here we run into problems - the method getIdentitydoesn't depend on any instance of the object, and should therefore be declared static(indeed, we may wish to refer to it from a static context). But if we put the getIdentitymethod into the interface then we can't declare it staticin the interface, so it can't be staticin any implementing class.

但是这里我们遇到了问题——该方法getIdentity不依赖于对象的任何实例,因此应该被声明static(实际上,我们可能希望从静态上下文中引用它)。但是如果我们把getIdentity方法放到接口中,那么我们就不能static在接口中声明它,所以它不能static在任何实现类中。

Is there any way of implementing this getIdentitymethod that:

有什么方法可以实现这种getIdentity方法:

  1. Forces consistency over all implementations of GroupElement, so that every implementation of GroupElementis forced to include a getIdentityfunction.
  2. Behaves statically; i.e., we can get the identity element for a given implementation of GroupElementwithout instantiating an object for that implementation.
  1. 强制 的所有实现的一致性GroupElement,以便GroupElement强制 的每个实现都包含一个getIdentity函数。
  2. 静态行为;即,我们可以获取给定实现的标识元素,GroupElement而无需为该实现实例化对象。

Condition (1) is essentially saying 'is abstract' and condition (2) is saying 'is static', and I know that staticand abstractare incompatible in Java. So are there some related concepts in the language that can be used to do this?

条件(1)基本上等于在说“是抽象的”和条件(2)是说“是静态的”,我知道static,并abstract在Java不兼容。那么语言中是否有一些相关的概念可以用来做到这一点?

采纳答案by Jason C

Essentially what you are asking for is the ability to enforce, at compile time, that a class defines a given static method with a specific signature.

本质上,您要求的是在编译时强制执行类定义具有特定签名的给定静态方法的能力。

You cannot really do this in Java, but the question is: Do you really need to?

在 Java 中你不能真正做到这一点,但问题是:你真的需要这样做吗?

So let's say you take your current option of implementing a static getIdentity()in each of your subclasses. Consider that you won't actually need this method until you useit and, of course, if you attempt to use it but it isn't defined, you willget a compiler error reminding you to define it.

因此,假设您选择getIdentity()在每个子类中实现静态。考虑到在您使用它之前实际上并不需要这个方法,当然,如果您尝试使用它但没有定义它,您收到一个编译器错误,提醒您定义它。

If you define it but the signature is not "correct", and you attempt to use it differently than you have defined it, you will also already get a compiler error (about calling it with invalid parameters, or a return type issue, etc.).

如果您定义了它但签名不“正确”,并且您尝试以不同于您定义它的方式使用它,您也会收到编译器错误(关于使用无效参数调用它,或返回类型问题等。 )。

Since you can't call subclassed static methods through a base type, you're alwaysgoing to have to call them explicitly, e.g. GInteger.getIdentity(). And since the compiler will already complain if you try and call GInteger.getIdentity()when getIdentity()isn't defined, or if you use it incorrectly, you essentially gain compile-time checking. The only thing you're missing, of course, is the ability to enforce that the static method is defined even if you never use it in your code.

由于您不能通过基类型调用子类静态方法,因此您总是必须显式调用它们,例如GInteger.getIdentity(). 并且由于如果您尝试调用GInteger.getIdentity()whengetIdentity()未定义,或者如果您使用不当,编译器就会抱怨,因此您基本上获得了编译时检查。当然,您唯一缺少的是强制定义静态方法的能力,即使您从未在代码中使用它。

So what you have already is pretty close.

所以你已经非常接近了。

Your example is a good example that explains whatyou want, but I would challenge you to come up with an example where having a compile-time warning about a missing static function is a necessity; the only thing I can think of that sort of comes close is if you are creating a library for use by others and you want to ensure that you don't forget to implement a particular static function -- but proper unit testing of all your subclasses can catch that during compile-time as well (you couldn't test a getIdentity()if it wasn't present).

你的例子是一个很好的例子,可以解释你想要什么,但我会挑战你想出一个例子,其中有一个关于缺少静态函数的编译时警告是必要的;我能想到的唯一一件事是,如果您正在创建一个供其他人使用的库,并且您想确保不会忘记实现特定的静态函数——但要对所有子类进行适当的单元测试也可以在编译时捕获它(getIdentity()如果它不存在,则无法测试)。

Note: Looking at your new question comment: If you are asking for the ability to calla static method given a Class<?>, you cannot, per se (without reflection) -- but you can still get the functionality you want, as described in Giovanni Botta's answer; you will sacrifice compile-time checks for runtime-checks but gain the ability to write generic algorithms using identity. So, it really depends on your end goal.

注意:查看您的新问题评论:如果您要求能够调用给定 a 的静态方法Class<?>,则您本身不能(没有反射) - 但您仍然可以获得所需的功能,如Giovanni Botta 中所述回答; 您将牺牲运行时检查的编译时检查,但获得使用身份编写通用算法的能力。所以,这真的取决于你的最终目标。

回答by Leo

" the method getIdentity doesn't dependonany instance of the object, and should therefore be declared static"

“ getIdentity 方法不依赖于对象的任何实例,因此应声明为静态”

Actually, if it does not depend on any instance, it can just return some constant value, it does not have to be static.

实际上,如果它不依赖于任何实例,它可以只返回一些常量值,它不必是静态的。

Just because a static method does not depend on an instance, it does not mean you should use it always for this kind of situation.

仅仅因为静态方法不依赖于实例,并不意味着您应该始终在这种情况下使用它。

回答by Ray

A mathematical group only has one characteristic operation, however a Java class can have any number of operations. Therefore these two concepts don't match.

一个数学组只有一个特征运算,但是一个 Java 类可以有任意数量的运算。因此这两个概念不匹配。

I can imagine something like a Java class Groupconsisting of a Setof elements and a specific operation, which would be an interface by itself. Something like

我可以想象一个 Java 类,Group它由一个Set元素和一个特定的操作组成,它本身就是一个接口。就像是

public interface Operation<E> {
   public E apply(E left, E right);
}

With that, you can build your group:

有了这个,你可以建立你的组:

public abstract class Group<E, O extends Operation<E>> {
    public abstract E getIdentityElement();
}

I know this is not entirely what you had in mind, but as I stated above, a mathematical group is a somewhat different concept than a class.

我知道这并不完全是你的想法,但正如我上面所说的,数学组是一个与类有些不同的概念。

回答by Giovanni Botta

There is no java way of doing this (you might be able to do something like that in Scala) and all the workarounds you will find are based on some coding convention.

没有 Java 方法可以做到这一点(您可能可以在 Scala 中做类似的事情),您会发现的所有解决方法都基于某些编码约定。

The typical way in which this is done in Java is to have your interface GroupElementdeclare two static methods such as this:

在 Java 中完成此操作的典型方法是让您的接口GroupElement声明两个静态方法,例如:

public static <T extends GroupElement> 
  T identity(Class<T> type){ /* implementation omitted */ }

static <T extends GroupElement> 
  void registerIdentity(Class<T> type, T identity){ /* implementation omitted */ }

You can easily implement those methods by using a class to instance mapor a home grown solution of choice. The point is you keep a static map of identity elements, one per each GroupElementimplementation.

您可以通过使用类来实例映射或选择的本地解决方案来轻松实现这些方法。关键是您保留一个身份元素的静态映射,每个GroupElement实现一个。

And here comes the need for a convention: each subclass of GroupElementwill have to statically declare its own identity element, e.g.,

这里需要一个约定:每个子类GroupElement都必须静态声明自己的标识元素,例如,

public class SomeGroupElement implements GroupElement{
  static{
    GroupElement.registerIdentity(SomeGroupElement.class, 
      /* create the identity here */);
  }
}

In the identitymethod you can throw a RuntimeExceptionif the identity was never registered. This won't give you static checking but at least runtime checking for your GroupElementclasses.

在该identity方法中,RuntimeException如果身份从未注册过,您可以抛出一个。这不会为您提供静态检查,但至少不会为您的GroupElement类提供运行时检查。

The alternative to this is a little more verbose and requires you to instantiate your GroupElementclasses through a factory only, which will also take care of returning the identity element (and other similar objects/functions):

对此的替代方法有点冗长,并且要求您GroupElement仅通过工厂实例化您的类,这也将负责返回标识元素(和其他类似的对象/函数):

public interface GroupElementFactory<T extends GroupElement>{
  T instance();
  T identity();
}

This is a pattern typically used in enterprise applications when the factory is injected through some dependency injection framework (Guice, Spring) in the application and it might be too verbose, harder to maintain and maybe overkill for you.

当工厂通过应用程序中的某些依赖注入框架(Guice、Spring)注入时,这是企业应用程序中通常使用的一种模式,它可能过于冗长、难以维护并且可能对您来说太过分了。

EDIT: After reading some of the other answers, I agree that you should model at the group level, not the group element level, since element types could be shared between different groups. Nonetheless, the above answers provides a general pattern to enforce the behavior you describe.

编辑:阅读其他一些答案后,我同意您应该在组级别而不是组元素级别进行建模,因为元素类型可以在不同组之间共享。尽管如此,上述答案提供了一个通用模式来强制您描述的行为。

EDIT 2: By "coding convention" above, I mean having a static method getIdentityin each subclass of GroupElement, as mentioned by some. This approach has the down side of not allowing generic algorithms to be written against the group. Once again, the best solution to that is the one mentioned in the first edit.

编辑 2:通过上面的“编码约定”,我的意思是getIdentity在 的每个子类中都有一个静态方法GroupElement,正如一些人所提到的。这种方法的缺点是不允许针对组编写通用算法。再一次,最好的解决方案是第一个编辑中提到的解决方案。

回答by Nikos Paraskevopoulos

There may be some misunderstaning in your reasoning. You see a mathematical "Group" is as you define it (if I can remember well); but its elements are not characterized by the fact that they belong to this group. What I mean is that an integer (or real) is a standalone entity, that also belongs to Group XXX (among its other properties).

您的推理中可能存在一些误解。你看到一个数学“组”是你定义的(如果我没记错的话);但它的元素的特征并不在于它们属于这个群体。我的意思是整数(或实数)是一个独立的实体,它也属于 XXX 组(以及它的其他属性)。

So, in the context of programming, I would separate the definition (class) of a Group form that of its members, probably using generics:

因此,在编程的上下文中,我class可能会使用泛型将 Group的定义 ( ) 与其成员的定义分开:

interface Group<T> {
    T getIdentity();
    T compose(T, T);
}

Even more analytic definition would be:

更具分析性的定义是:

/** T1: left operand type, T2: right ..., R: result type */
interface BinaryOperator<T1, T2, R> {
    R operate(T1 a, T2 b);
}

/** BinaryOperator<T,T,T> is a function TxT -> T */
interface Group<T, BinaryOperator<T,T,T>> {
    void setOperator(BinaryOperator<T,T,T> op);
    BinaryOperator<T,T,T> getOperator();
    T getIdentity();
    T compose(T, T); // uses the operator
}

All that is an idea; I haven't actually touched math for a long time, so I could be wildly wrong.

所有这些都是一个想法;我已经很长时间没有真正接触数学了,所以我可能会大错特错。

Have fun!

玩得开心!

回答by ajb

If you need the ability to generate an identity where the class isn't known at compile time, the first question is, how do you know, at run time, what class you want? If the class is based on some other object, then I think the cleanest way is to define a method in the superclass that means "get an identity whose class is the same as" some other object.

如果您需要能够在编译时不知道类的情况下生成标识,那么第一个问题是,您如何在运行时知道您想要什么类?如果该类基于某个其他对象,那么我认为最简洁的方法是在超类中定义一个方法,这意味着“获取与某个其他对象的类相同的身份”。

public GroupElement getIdentitySameClass();

That would have to be overridden in each subclass. The override would probably not use the object; the object would be used only to select the correct getIdentityto call polymorphically. Most likely, you'd also want a static getIdentityin each class (but there's no way I know of for the compiler to force one to be written), so the code in the subclass would probably look like

这必须在每个子类中被覆盖。覆盖可能不会使用该对象;该对象将仅用于选择正确的getIdentity多态调用。最有可能的是,您还希望getIdentity在每个类中都有一个静态(但我知道编译器无法强制编写一个),因此子类中的代码可能看起来像

public static GInteger getIdentity() { ... whatever }

@Override
public GInteger getIdentitySameClass() { return getIdentity(); }

On the other hand, if the class you need comes from a Class<T>object, I think you'll need to use reflection starting with getMethod. Or see Giovanni's answer, which I think is better.

另一方面,如果您需要的类来自一个Class<T>对象,我认为您需要使用以getMethod. 或者看看乔瓦尼的回答,我认为更好。

回答by Colin

We all agree, if you want to implement groups you are going to need a group interface and classes.

我们都同意,如果你想实现组,你将需要一个组接口和类。

public interface Group<MyGroupElement extends GroupElement>{
    public MyGroupElement getIdentity()
}

We implement the groups as singletonsso we can access getIdentitystatically through instance.

我们将组实现为单例,以便我们可以getIdentity通过instance.

public class GIntegerGroup implements Group<GInteger>{

    // singleton stuff
    public final static instance = new GIntgerGroup();
    private GIntgerGroup(){};

    public GInteger getIdentity(){
        return new GInteger(0);
    }
}

public class PosRealGroup implements Group<PosReal>{

    // singleton stuff
    public final static instance = new PosRealGroup();
    private PosRealGroup(){}        

    public PosReal getIdentity(){
        return new PosReal(1);
    }
}

if we also need to be able to get the identity from a group element, I would update your GroupElementinterface with:

如果我们还需要能够从组元素中获取身份,我会用以下内容更新您的GroupElement界面:

public Group<GroupElement> getGroup();

and GInteger with:

和 GInteger 与:

public GIntegerGroup getGroup(){
    return GIntegerGroup.getInstance(); 
}

and PosReal with:

和 PosReal:

public PosRealGroup getGroup(){
    return PosRealGroup.getInstance(); 
}