1

Example:

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

As my CityEntity is of type AbstractEntity, why can't I do that in my cityConverter?

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

I guess the solution is to cast, but it's not ellegant:

 @Override
public CityDTO convert(AbstractEntity entity) { 
  CityEntity cityEntity = (CityEntity) entity;
}
Tyvain
  • 2,640
  • 6
  • 36
  • 70

2 Answers2

3

You can't reduce the allowed parameter type, because you would break Liskov substitution principle. If one calls convert on an unknown AbstractConverter implementation (due to polymorphy), (s)he would guess that he can always pass any AbstractEntity implementation. This wouldn't be the case if CityConverter only allows a very specific subtype.

Now about your code:

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

The first thing that wonders me here is: why is AbstractEntity a fixed type here? I would name the converter class AbstractEntityConverter or something like this if I always want to convert AbstractEntity instances into something different.

So I guess you really want something like this:

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

Where F is the "from" type which acts as the source and T is the target type, which will be returned. So you can let the AbstractConverter subtypes decide what they likes to convert.

Then you have this:

class CityConverter extends AbstractConverter {
}

Why do you use AbstractConverter as a raw type here? Shouldn't it be?

class CityConverter extends AbstractConverter<CityDTO> {
}

But anyway, if you like to use my suggestion, then also add the source type:

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

This would work.

Community
  • 1
  • 1
Tom
  • 16,842
  • 17
  • 45
  • 54
2

Your CityConverter extends AbstractConverter and AbstractConverter<T> requires implementation for

public abstract T convert(AbstractEntity e);

It is not a problem that extends AbstractConverter uses raw-types because overridden method can be more specific in case of returned object (since it is still of type described by parent class).

But problem appears when in derived class you want to require more specific type as argument.

Remember that derived class can still be used from reference which is of one of parents type like it is possible to have code like:

AbstractConverter ac = new CityConverter();

So when we will invoke ac.convert(...) compiler will allow us to use any type of AbstractEntity as argument, not only CityEntity which could brake code of CityConverter#convert.

Which is why we can't declare more specific type of method argument when overriding it.

Now about question from your title:

Why do I need cast in this case?
...

CityEntity cityEntity = (CityEntity) entity;

you need casting because entity is declared to be AbstractEntity which means there is a chance that passed instance may not be of type CityEntity so compiler can't compile code like:

CityEntity cityEntity = entity;//error without cast

By casting you are basically saying "I am sure that passed instance will be of type (CityEntity) and if not, I am willing to face consequences and am ready to see my application stopped by ClassCastException".

Pshemo
  • 122,468
  • 25
  • 185
  • 269