【问题标题】:Why can derived classes only sometimes have specialized types in overridden methods?为什么派生类有时只能在重写方法中具有专门的类型?
【发布时间】:2009-04-22 11:33:12
【问题描述】:

假设我有这个基类:

abstract public class Base {
    abstract public Map save();
    abstract public void load(Map data);
}

令我惊讶的是,我可以在派生类中做到这一点:

public class Derived extends Base {
    @Override
    public Map<String, String> save() {    //Works
        ...
    }
    ...
}       

但我不能这样做:

public class Derived extends Base {
    @Override
    public void load(Map<String, String> data) {    // Fails
        ...
    }
    ...
}       

这里发生了什么?为什么我可以使用专门的返回类型而不是专门的参数类型?

更令人困惑的是,如果我保留load 的原始声明,我可以将其分配给更特殊的类型:

public class Derived extends Base {
    @Override
    public void load(Map data) {
        Map<String, String> myData = data;   // Works without further casting
        ...
    }
    ...
}       

【问题讨论】:

    标签: java polymorphism


    【解决方案1】:

    从专用类型到原始类型的隐式转换 - 这始终是“安全的”,因为使用原始类型的人无法做出任何假设。因此,希望从方法中返回原始 Map 的人不会介意他们是否获得 Map&lt;String, String&gt;

    没有从原始类型到专用类型的隐式转换 - 如果有人将原始 Map 传递给 load,它可能具有非字符串键和值。这在load 的基类型声明中是完全合法的。

    抛开泛型不谈,你的方法有点像这样:

    public abstract class Base
    {
        public abstract Object save();
        public abstract void load(Object x);
    }
    
    public class Derived extends Base
    {
        @Override
        public String save() { ... } // Valid
    
        @Override
        public void load(String x) // Not valid
    }
    

    删除泛型后,是否清楚为什么 save 调用在这里可以,但 load 调用不是?考虑一下:

    Base b = new Derived();
    Object x = b.save(); // Fine - it might return a string
    b.load (new Integer(0)); // Has to compile - but the override wouldn't work!
    

    【讨论】:

      【解决方案2】:

      您不能将参数更改为load(Map&lt;String, String&gt; data),因为如果您使用基类而不是专门的类,则很容易违反这一点:

      Base base = new Derived()
      base.load(new HashMap<Integer, Integer>());
      

      这将调用派生的加载方法但违反声明。因为泛型只在编译时检查,所以无法检测到该错误。

      返回值没有问题,只要它们在子类中比在超类中更专业。

      【讨论】:

        【解决方案3】:

        我认为问题在于,通常可以有两种方法来区分参数类型。恰好需要选择一个。这需要查看未擦除的类型。这意味着这两个load 方法是可区分的。所以我们有可区分的方法,但在擦除的形式中只有一种方法存在。

        这不是返回类型的问题,因为(在 Java 中)您可以拥有协变返回类型,但不能通过返回类型重载。

        【讨论】:

        • 我在回答中给出的问题也不是问题吗?
        猜你喜欢
        • 2014-07-25
        • 1970-01-01
        • 2010-10-22
        • 2019-01-08
        • 1970-01-01
        • 2010-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多