简体   繁体   English

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

[英]How can I implement abstract static methods in Java?

There are numerous questions about the impossibility of including static abstract Java methods. 关于包含静态抽象Java方法的不可能性存在许多问题。 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. 但我找不到任何关于我即将陈述的具体问题。

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. 在我看来,制作Java的人和很多使用Java的人并没有像我和其他许多人那样思考静态方法 - 作为类函数或属于类的方法而不是任何对象。 So is there some other way of implementing a class function? 那么还有其他一些实现类函数的方法吗?

Here is my example: in mathematics, a group is 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 e such that x * e = x for all x in the group. 然而,有一组具有另一个重要的特性:每个组都有一个标识元素 -一个元素e X * E = X为组中所有的x。 For example, the identity element for positive reals under multiplication is 1 , and the identity element for integers under addition is 0 . 例如,乘法下正实数的标识元素是1 ,加法下整数的标识元素是0 In that case, it makes sense to have a method for each implementing class like the following: 在这种情况下,为每个实现类创建一个方法是有意义的,如下所示:

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

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

But here we run into problems - the method getIdentity doesn'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). 但是这里我们遇到了问题 - 方法getIdentity不依赖于对象的任何实例,因此应该声明为static (实际上,我们可能希望从静态上下文中引用它)。 But if we put the getIdentity method into the interface then we can't declare it static in the interface, so it can't be static in any implementing class. 但是如果我们将getIdentity方法放入接口,那么我们就不能在接口中声明它是static的,所以它在任何实现类中都不能是static

Is there any way of implementing this getIdentity method that: 有没有办法实现这个getIdentity方法:

  1. Forces consistency over all implementations of GroupElement , so that every implementation of GroupElement is forced to include a getIdentity function. 强制在GroupElement所有实现上保持一致性,以便强制GroupElement每个实现都包含getIdentity函数。
  2. Behaves statically; 静态地行事; ie, we can get the identity element for a given implementation of GroupElement without instantiating an object for that implementation. 也就是说,我们可以获取GroupElement的给定实现的identity元素,而无需实例化该实现的对象。

Condition (1) is essentially saying 'is abstract' and condition (2) is saying 'is static', and I know that static and abstract are incompatible in Java. 条件(1)本质上是说'是抽象'而条件(2)是'静态',我知道staticabstract在Java中是不兼容的。 So are there some related concepts in the language that can be used to do this? 那么语言中是否有一些可用于执行此操作的相关概念?

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. 因此,假设您采用当前在每个子类中实现静态getIdentity()选项。 Consider that you won't actually need this method until you use it and, of course, if you attempt to use it but it isn't defined, you will get a compiler error reminding you to define it. 考虑到在使用它之前实际上不需要这种方法,当然,如果您尝试使用它但未定义它,您收到编译器错误,提醒您定义它。

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 always going to have to call them explicitly, eg GInteger.getIdentity() . 由于您无法通过基类型调用子类静态方法,因此您始终必须显式调用它们,例如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. 并且,如果在GInteger.getIdentity() getIdentity() GInteger.getIdentity()时尝试调用GInteger.getIdentity() ,或者如果使用不正确,编译器将会抱怨,您实际上会获得编译时检查。 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. 当然,您唯一缺少的是能够强制执行静态方法,即使您从未在代码中使用过静态方法。

So what you have already is pretty close. 所以你已经非常接近了。

Your example is a good example that explains what you 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()如果不存在,则无法测试getIdentity() )。

Note: Looking at your new question comment: If you are asking for the ability to call a 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 ; 注意:查看您的新问题评论:如果您要求能够在给定Class<?> 调用静态方法,那么您本身就不能(没有反射) - 但您仍然可以获得所需的功能,如在Giovanni Botta的回答中描述; 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 mathematical group only has one characteristic operation, however a Java class can have any number of operations. 数学组只有一个特征操作,但Java类可以有任意数量的操作。 Therefore these two concepts don't match. 因此这两个概念不匹配。

I can imagine something like a Java class Group consisting of a Set of elements and a specific operation, which would be an interface by itself. 我可以想象类似Java类的Group它由一Set元素和一个特定的操作组成,它本身就是一个接口。 Something like 就像是

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. 我知道这并不完全是你想到的,但正如我上面所说,一个数学小组是一个有点不同的概念而不是一个类。

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: 因此,在编程的上下文中,我将分离其成员的Group形式的定义( class ),可能使用泛型:

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! 玩得开心!

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 GroupElement declare 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 map or a home grown solution of choice. 您可以使用类到实例映射或选择的本地解决方案轻松实现这些方法。 The point is you keep a static map of identity elements, one per each GroupElement implementation. 关键是你要保留一个静态的身份元素图,每个GroupElement实现一个。

And here comes the need for a convention: each subclass of GroupElement will have to statically declare its own identity element, eg, 这需要一个约定: GroupElement每个子类都必须静态声明自己的标识元素,例如,

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

In the identity method you can throw a RuntimeException if the identity was never registered. identity方法中,如果从未注册过身份,则可以抛出RuntimeException This won't give you static checking but at least runtime checking for your GroupElement classes. 这不会给你静态检查,但至少会对你的GroupElement类进行运行时检查。

The alternative to this is a little more verbose and requires you to instantiate your GroupElement classes 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 getIdentity in each subclass of GroupElement , as mentioned by some. 编辑2 :通过上面的“编码约定”,我的意思是在GroupElement每个子类中都有一个静态方法getIdentity ,如某些人所述。 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. 再次,最好的解决方案是第一次编辑中提到的解决方案。

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 getIdentity to call polymorphically. 该对象仅用于选择正确的getIdentity以进行多态调用。 Most likely, you'd also want a static getIdentity in 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 (但我不知道编译器是否强制编写一个),因此子类中的代码可能看起来像

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 . 另一方面,如果你需要的类来自Class<T>对象,我认为你需要使用以getMethod开头的反射。 Or see Giovanni's answer, which I think is better. 或者看看Giovanni的答案,我觉得这个答案更好。

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 singletons so we can access getIdentity statically through instance . 我们将组实现为单例,因此我们可以通过instance静态访问getIdentity

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 GroupElement interface 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(); 
}

" the method getIdentity doesn't dependonany instance of the object, and should therefore be declared static" “方法getIdentity不依赖于对象的任何实例,因此应该声明为static”

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. 仅仅因为静态方法不依赖于实例,并不意味着您应该始终将它用于此类情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM