Java:覆盖方法时替换参数的子类/子类型?

发布于 2024-10-06 20:41:34 字数 813 浏览 0 评论 0原文

所以我之前问过这个问题,但我在大多数人发现的代码中犯了一个错误,而不是问题本身。

无论如何,我正在尝试重写类中的接口方法。但是,我希望重写方法中的参数类型是重写方法中定义的参数类型的子类。

接口是:

public interface Observer {
 public void update(ComponentUpdateEvent updateEvent) throws Exception;
}

而重写此方法的类是:

public class ConsoleDrawer extends Drawer {

//...

 @Override
 public void update(ConsoleUpdateEvent updateEvent) throws Exception {
  if (this.componentType != updateEvent.getComponentType()) {
   throw new Exception("ComponentType Mismatch.");
  }
  else {
   messages = updateEvent.getComponentState(); 
  }
 }

//...

}

ConsoleUpdateEvent 是 ComponentUpdateEvent 的子类。

现在,我可以让 ConsoleDrawer 中的 update() 方法将 ComponentUpdateEvent 作为参数,然后将其转换为 ConsoleUpdateEvent,但如果可能的话,我正在寻找稍微更优雅的解决方案。任何帮助将不胜感激。谢谢。

So I asked this question before but I had a mistake in the code which most people picked up on, rather than the problem itself.

Anyway, I'm trying to override an interface method in a class. However, I want the type of the parameter in the overriding method to be a subclass of the type of the parameter as defined in the overriden method.

The interface is:

public interface Observer {
 public void update(ComponentUpdateEvent updateEvent) throws Exception;
}

While the class that overrides this method is:

public class ConsoleDrawer extends Drawer {

//...

 @Override
 public void update(ConsoleUpdateEvent updateEvent) throws Exception {
  if (this.componentType != updateEvent.getComponentType()) {
   throw new Exception("ComponentType Mismatch.");
  }
  else {
   messages = updateEvent.getComponentState(); 
  }
 }

//...

}

ConsoleUpdateEvent is a subclass of ComponentUpdateEvent.

Now, I could just have the update() method in ConsoleDrawer take a ComponentUpdateEvent as a parameter and then cast it to a ConsoleUpdateEvent but I'm looking for a slightly more elegant solution if possible. Anyhelp would be appreciated. Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

难以启齿的温柔 2024-10-13 20:41:34

你不能。这不是埃菲尔铁塔。问题是您可以使用接口来调用具有不兼容类型的实现方法。因此不允许使用协变参数。逆变参数也是不允许的,但提供重载更容易。允许协变返回类型(自 1.5 起)。

您可以参数化接口:

public interface Observer<T extends ComponentEvent> {
    void update(T event) throws Exception;
}

或者,使用更有意义的接口:

public interface ConsoleObserver {
    void update(ConsoleEvent event) throws Exception;
}

You can't. This is not Eiffel. The problem being that you could use the interface to call the implementation method with an incompatible type. So covariant parameters are not allowed. Contravariant parameters aren't allowed either, but it is easier to provide an overload. Covariant return type is allowed (since 1.5).

You could parameterise the interface:

public interface Observer<T extends ComponentEvent> {
    void update(T event) throws Exception;
}

Alternatively, use a more meaningful interface:

public interface ConsoleObserver {
    void update(ConsoleEvent event) throws Exception;
}
鹿港小镇 2024-10-13 20:41:34

根据 OOP 原则,子类应该以与父类完全相同的方式使用。
例如

Observer ob = new ConsoleDrawer();
ob.update(new ComponentUpdateEvent()); // This needs to work always.

,但是,如果 Java 允许您在重写方法时使用参数的子类型,那么它会将您的代码暴露给重写方法(在子类中)拒绝输入参数的情况(在上述情况下为 ComponentUpdateEvent) 。因此,您永远无法确定在观察者引用上调用 update() 是否安全。

因此,唯一合乎逻辑的解决方案是接受父类参数,对其进行类型检查,然后将其转换为所需的子类型。

Going by OOP principles, a sub-class should be usable in exactly the same way as the parent class.
e.g.

Observer ob = new ConsoleDrawer();
ob.update(new ComponentUpdateEvent()); // This needs to work always.

However, if Java were to allow you to use a subtype of the parameter when overriding a method, then it would expose your code to cases where the overriding method (in the subclass) would reject the input parameter (ComponentUpdateEvent in the above case). Thus, you would never be sure whether its safe to call update() or not on an Observer reference.

Hence, the only logical solution is to accept the parent class parameter, type-check it and then cast it to the required subtype.

惯饮孤独 2024-10-13 20:41:34

你可以尝试以下方法。如果编译器知道您将调用第一个方法而不是第二个方法,则 @Deprecated 会生成警告。

@Override @Deprecated
public void update(ComponentUpdateEvent updateEvent) {
    // throws a ClassCastException if its not the right type.
    update((ConsoleUpdateEvent) updateEvent); 
}

public void update(ConsoleUpdateEvent updateEvent) {
    messages = updateEvent.getComponentState(); 
}

顺便说一句:您不应该只在所有内容上放置抛出异常。这当然不是最佳实践。

编辑:我已经针对这个问题实现了一个不同的解决方案,它与 OSGi 配合良好,但可以在任何地方工作。

观察者向代理注册自己,并期望找到带有注释的方法,例如 ObserverCallback。

例如

public class ConsoleDrawer extends Drawer {
 @ObserverCallback
 public void onConsoleUpdateEvent(ConsoleUpdateEvent updateEvent) {
   messages = updateEvent.getComponentState(); 
 }
}

public class DeviceDrawer extends Drawer {
 @ObserverCallback
 public void onDeviceUpdateEvent(DeviceUpdateEvent updateEvent) {
   // do something.
 }
}

,在第一种情况下,代理找到带有 @ObserverCallback 的方法,该方法采用一个参数。这是 Broker 将传递的唯一类型。第二类期望不同的类型。观察者可以有多种方法/类型,允许他们以适合该类型的不同方法处理不同的消息。您还知道您永远不会收到您不期望的数据类型。

You could try the following. The @Deprecated produces a warning if the compiler knows you will be calling the first method rather than the second.

@Override @Deprecated
public void update(ComponentUpdateEvent updateEvent) {
    // throws a ClassCastException if its not the right type.
    update((ConsoleUpdateEvent) updateEvent); 
}

public void update(ConsoleUpdateEvent updateEvent) {
    messages = updateEvent.getComponentState(); 
}

BTW: You shouldn't just place throws Exception on everything. Its certainly not best practice.

EDIT: I have implemented a different solution to this problem which works well with OSGi but can work anywhere.

An Observer registers itself with a Broker and expects to find methods with an annotation such as ObserverCallback.

e.g.

public class ConsoleDrawer extends Drawer {
 @ObserverCallback
 public void onConsoleUpdateEvent(ConsoleUpdateEvent updateEvent) {
   messages = updateEvent.getComponentState(); 
 }
}

public class DeviceDrawer extends Drawer {
 @ObserverCallback
 public void onDeviceUpdateEvent(DeviceUpdateEvent updateEvent) {
   // do something.
 }
}

In the first case, the broker finds a method with the @ObserverCallback which takes one argument. This is the only type the Broker will pass it. The second class expects a different type. Observers can have multiple method/types allowing them to handle different messages in different methods appropriate to that type. You also know you will never receive a data type you don't expect.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文