【问题标题】:How to specify method parameters while overriding without casting?如何在不强制转换的情况下指定方法参数?
【发布时间】:2017-02-21 18:51:07
【问题描述】:

我有一个关于方法覆盖的问题,它可能受到其他方面的影响,所以我希望这个问题足够具体。 上下文:我尝试为某些对象的价格构建一个简单的计算器,但“价格种类”应与其余逻辑分离,以便客户端只需要知道以下接口。

我认为最好用我的问题开始的基本代码来解释。

public interface InterfacePrice {
    public InterfacePrice addPrices(InterfacePrice price1, InterfacePrice price2);
    public InterfacePrice subtractPrices(InterfacePrice price1, InterfacePrice price2);
    public String priceToString();
}

所以这就是我应该能够用两个价格做的事情。 现在我试着再详细说明一下:

public class BnSPrice implements InterfacePrice {
    int gold = 0;
    int silver = 0;
    int copper = 0;
    //the following will obviously not compile but i hope it helps explain
    @Override
    public InterfacePrice addPrices(BnSPrice price1, BnSPrice price2) {
    return null;
    [...more code here...]
}

所以我想做的是基于 100 铜是 1 银的事实编写一个简单的添加方法,依此类推。现在我的问题: 编译器不允许我将参数更改为我理解的 BnSPrice 类型,我只是想说明问题。如果我不知道它们具有正确的类型,那么尝试添加两个具体价格的黄金和白银是没有意义的,所以我首先想到的要获得正确的行为就是用普通的 InterfacePrice 覆盖该方法参数并在方法开始时进行类型检查并转换为适当的类型,但这似乎不是很有效或美观。

问题:当我的客户端代码只需要知道接口时,如何更改设计以灵活地添加其他价格模型? (我只是在学习java,如果问题有一些我看不到的明显答案,请原谅我。)可以通过使用泛型来解决吗? (我对这些了解不多……我正在努力让泛型的用法进入我的脑海。)

提前感谢您的帮助。

【问题讨论】:

  • 那么您的问题是如何只允许使用兼容的价格对象(例如黄金和白银)而不是不兼容的类型调用 addPrice?或者只是如何添加两个使用相同接口的不同对象?
  • 首先,davidxxx 的回答对我遇到的问题很明显。

标签: java generics inheritance interface


【解决方案1】:

由于您想在派生类中应用,对父方法的参数进行一些限制,泛型可以帮助您。

使用泛型,您还可以选择在方法中返回参数化类型或InterfacePrice

在你的具体类示例中,你写:

@Override
public InterfacePrice addPrices(BnSPrice price1, BnSPrice price2) {

我将给出一个例子,addPrices() 方法遵循这种方式,而对于subtractPrices() 方法,我将使用我们想要返回特定类型的方式。

你可以创建一个通用接口:

public interface InterfacePrice<T extends InterfacePrice<T>> {
    public InterfacePrice<T> addPrices(T price1, T price2);
    public T subtractPrices(T price1, T price2);
    public String priceToString();
}

这是它的一个实现:

public class BnSPrice implements InterfacePrice<BnSPrice> {
    int gold = 0;
    int silver = 0;
    int copper = 0;

    @Override
    public InterfacePrice<?> addPrices(BnSPrice price1, BnSPrice price2) {
        return ...;
    }

    @Override
    public BnSPrice subtractPrices(BnSPrice price1, BnSPrice price2) {
        return ...;
    }

    @Override
    public String priceToString() {
        return ...;
    }
}

【讨论】:

  • 啊啊啊,有道理!感谢您简洁明了的回答;这真的帮助了我!现在我有一个小的后续问题:我描述的问题的结构是泛型的主要目的吗?
  • 不客气 :) 显然,如果您按接口编程并在处理代码中操作接口,则无需在实际代码中执行 instanceof 和强制转换。泛型的这种使用在某种程度上可以弥补 Java 中方法覆盖/实现的不足,其中重载/实现的方法的参数类型必须与原始定义中定义的参数类型相同。在某些情况下,这是可取的,因为您认为对参数类型应用此约束很重要。在其他情况下,这是一种开销,并且无缘无故地违反了 liskov 原则。
  • 嗯,好的..我搜索了 liskov 原理。您对我的问题的解决方案没有违反这一点还是我错了?
  • 嗯。你可能适合 Liskov。其实我想说的是,你不能再通过接口编程了。此代码无法编译:InterfacePrice&lt;? extends InterfacePrice&lt;?&gt;&gt; price = new BnSPrice(); price.addPrices(new BnSPrice(), new BnSPrice()); 而没有泛型使用此代码编译:InterfacePrice price = new BnSPrice(); price.addPrices(new BnSPrice(), new BnSPrice());
  • 对不起,我的回答迟到了;我不太了解您的某些事情。IntelliJ 向我显示“无法解析符号:addPrice”-错误/警告,因此它不会像您说的那样编译(我试图测试您的第一个示例,但略有不同,addPrices ->addPrice,只有一个参数)。但是为什么 addPrice 方法对每个 InterfacePrice 都可用时会出现这种情况。对于您的最后评论,我可能没有正确理解,如果我失去对接口而不是特定实现进行编程的能力,我不确定使用泛型会赢得什么。
猜你喜欢
  • 2023-03-16
  • 2013-05-23
  • 2020-08-08
  • 2014-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-13
  • 2019-07-23
相关资源
最近更新 更多