【问题标题】:Specializing method arguments in subclasses in Java在 Java 的子类中专门化方法参数
【发布时间】:2016-11-25 15:30:40
【问题描述】:

考虑以下情况:

public abstract class AnimalFeed{
}
public class FishFeed extends AnimalFeed{
}
public class BirdFeed extends AnimalFeed{
}

public abstract class Animal{

public void eat(AnimalFeed somethingToEat)

}

现在我想定义一个扩展“动物”的“鸟”类,确保当鸟吃东西时,它只吃鸟食。

一种解决方案是指定一种合约,其中“吃”的调用者必须传递适当提要的实例

public class Bird extends Animal{

@Override 
public void eat(AnimalFeed somethingToEat){

    BirdFeed somethingGoodForABird

    if(somethingToEat.instanceOf(BirdFeed)){
    somethingGoodForABird = (BirdFeed) somethingGoodForABird
    }else{
    //throws error, complaining the caller didn't feed the bird properly
    }
}
}

将参数的责任委托给调用者是否可以接受?如何强制调用者传递参数的特化?是否有替代设计解决方案?

【问题讨论】:

    标签: java inheritance design-patterns polymorphism


    【解决方案1】:

    您需要在类中添加一个类型变量:

    public abstract class Animal<F extends AnimalFeed> {
      public abstract void eat(F somethingToEat);
    }
    

    然后您可以将您的子类声明为需要特定类型的AnimalFeed

    public class Bird extends Animal<BirdFeed> {
      public void eat(BirdFeed somethingToEat) {}
    }
    
    public class Fish extends Animal<FishFeed> {
      public void eat(FishFeed somethingToEat) {}
    }
    

    【讨论】:

      【解决方案2】:

      理论的角度来看,您所要求的没有意义。

      限制方法参数违反了Liskov Substitution Principle

      那里的想法:一些 base 类对象的任何出现(usage)也必须能够处理一些 sub 类对象.

      一个更简单的例子:当你的 base 接口去时:

      void foo(Number n)
      

      那么你不能这样做

      @Override
      void foo(Integer i)
      

      在一个子类中。因为突然来电了

      someObject.foo(someNumber)
      

      当 someObject 属于该派生类时会遇到丑陋的问题;它只接受整数,但不接受数字。

      换句话说:好的OO设计不仅仅是写下A extends B。您必须遵守这些规则;或者你最终会发现系统已经在概念点上被打破了!

      记录一下:理论上加宽方法参数是有效的(一般来说,但在 Java 中);并且限制方法的返回类型也是可以的(因为这些更改不会破坏客户端代码;甚至在 Java 中也有效)。

      长话短说:这里的答案是改变你的设计;例如,通过使用泛型和依赖接口以某种方式创建 Animal 和 Feed 类层次结构之间的关系。

      【讨论】:

      • "扩大方法参数非常有效" 一般来说,但在 Java 中不是这样:由于 Java 方法解析和重载的工作方式,您不能扩大参数类型。请参阅stackoverflow.com/questions/18337056/…(尽管我确信有比这更好的 Q 和 As)。
      • 我同意更宽的参数不会违反 LSP。我只是说 java 不支持更广泛的参数(而更窄的返回类型是;返回类型不是用于确定调用哪个方法的方法签名的一部分)。
      • 知道了;更新了我的答案。虽然我想知道为什么你的答案似乎比我的好六倍;-)
      • 一个用户可以直接关联和使用而无需或很少修改的示例使它们更好:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-09
      • 1970-01-01
      • 2014-02-06
      • 2018-01-25
      • 2017-01-08
      • 1970-01-01
      相关资源
      最近更新 更多