2

假设我有这个基类:

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
        ...
    }
    ...
}       
4

3 回答 3

11

从专用类型到原始类型的隐式转换 - 这始终是“安全的”,因为使用原始类型的人无法做出任何假设。因此,希望Map从方法中得到原始结果的人并不介意他们得到Map<String, String>.

没有从原始类型到专用类型的隐式转换 - 如果有人将原始类型传递给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!
于 2009-04-22T11:40:42.933 回答
2

您不能将参数更改为,load(Map<String, String> data)因为如果您使用基类而不是专用类,则很容易违反这一点,如下所示:

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

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

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

于 2009-04-22T11:39:38.100 回答
0

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

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

于 2009-04-22T12:26:53.697 回答