2016-02-05 4 views
1

Пример:Зачем мне нужен бросок в этом случае?

class AbstractConverter <T> { 
    public abstract T convert(AbstractEntity e); 
} 

class CityEntity extends AbstractEntity {...} 

class CityConverter extends AbstractConverter { 
    @Override 
    public CityDTO convert(CityEntity entity) {...} // why can't I do that?? 
} 

Как мой CityEntity имеет тип AbstractEntity, почему я не могу сделать это в моем cityConverter?

The method convert(CityEntity) of type CityConverter must override or implement a supertype method 

Я предполагаю, что решение бросить, но это не ellegant:

@Override 
public CityDTO convert(AbstractEntity entity) { 
    CityEntity cityEntity = (CityEntity) entity; 
} 
+0

'extends AbstractConverter' не указывает тип генериков. – Andreas

ответ

3

Вы не можете уменьшить разрешенный тип параметра, потому что вы сломаете Liskov substitution principle. Если вы назовете convert по неизвестной ошибке (из-за полиморфности), он предположил бы, что он всегда может пройти любую реализацию AbstractEntity. Это было бы не так, если CityConverter разрешает очень специфический подтип.

Теперь о вашем коде:

class AbstractConverter <T> { 
    public abstract T convert(AbstractEntity e); 
} 

Первое, что удивляется меня здесь: почему AbstractEntity фиксированного типа здесь?Я бы назвал класс преобразователя AbstractEntityConverter или что-то вроде этого, если я всегда хочу конвертировать AbstractEntity экземпляров во что-то другое.

Так что я думаю, что вы действительно хотите что-то вроде этого:

class AbstractConverter<F, T> { 
    public abstract T convert(F source); 
} 

Где F находится в «от» типа, который действует в качестве источника и T является целевым типом, который будет возвращен. Таким образом, вы можете позволить подтипам AbstractConverter решить, что им нравится конвертировать.

Тогда у вас есть это:

class CityConverter extends AbstractConverter { 
} 

Почему вы используете AbstractConverter как raw type здесь? Разве это не должно быть?

class CityConverter extends AbstractConverter<CityDTO> { 
} 

Но в любом случае, если вы хотите использовать мое предложение, а затем добавить тип источника:

class CityConverter extends AbstractConverter<CityEntity, CityDTO> { 
    @Override 
    public CityDTO convert(CityEntity entity) {...} 
} 

Это будет работать.

+0

Спасибо, я понимаю причину, а также решение работает отлично. – Tyvain

2

Ваш CityConverter extends AbstractConverter и AbstractConverter<T> требует реализации для

public abstract T convert(AbstractEntity e); 

Это не проблема, которая extends AbstractConverter использует сырье потому что переопределенный метод может быть более конкретным в случае возвращаемого объекта (поскольку он все еще относится к типу, описанному родительским классом).

Но проблема возникает, когда в производном классе вы хотите потребовать более конкретного типа в качестве аргумента.

Помните, что производный класс по-прежнему можно использовать из ссылки, которая одного из родителей типа, как можно иметь такой код:

AbstractConverter ac = new CityConverter(); 

Так что, когда мы будем ссылаться на ac.convert(...) компилятор позволит использовать любой тип AbstractEntity как аргумент, не только CityEntity который мог бы тормозить Код CityConverter#convert.

Именно поэтому мы не можем объявлять более конкретный тип аргумента метода при его переопределении.

Теперь о вопросе от Вашего названия:

Почему мне нужно бросить в этом случае?
...

CityEntity cityEntity = (CityEntity) entity; 

вам нужно литье, потому что entity объявляется AbstractEntity, который означает, что есть вероятность того, что передается экземпляр не может быть типа CityEntity поэтому компилятор не может скомпилировать код, как:

CityEntity cityEntity = entity;//error without cast 

Вы можете сказать: «Я уверен, что пройденный экземпляр будет иметь тип (CityEntity), а если нет, CastException».