Почему производные классы могут иногда иметь специализированные типы в переопределенных методах? - PullRequest
2 голосов
/ 22 апреля 2009

Допустим, у меня есть этот базовый класс:

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

Ответы [ 3 ]

11 голосов
/ 22 апреля 2009

Существует неявное преобразование из специализированного типа в необработанный тип - это всегда "безопасно", потому что кто-то, использующий необработанный тип, не может делать никаких предположений. Так что кто-то, ожидающий получить необработанный 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!
2 голосов
/ 22 апреля 2009

Вы не можете изменить параметры на load(Map<String, String> data), потому что вы могли бы легко нарушить это, если бы использовали базовый класс вместо специализированного класса, например так:

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

Это вызвало бы метод загрузки производного, но в нарушение объявления. Поскольку дженерики проверяются только во время компиляции, обнаружить эту ошибку невозможно.

Возвращаемые значения не проблема, если они более специализированы в подклассах, чем в суперклассах.

0 голосов
/ 22 апреля 2009

Я считаю, что проблема в том, что в общем случае можно использовать два метода, различаемых по типам аргументов. Точно нужно быть выбранным. Это требует рассмотрения неослабленных типов. Это означает, что два метода load различимы. Итак, у нас есть различимые методы, но в стертой форме существует только один метод.

Это не проблема с возвращаемыми типами, потому что (в Java) вы можете иметь ковариантные возвращаемые типы, но не перегружать возвращаемыми типами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...