Avoiding unsafe casting with generics - java

Lets say I have an abstract class:
public abstract class Trainer<T extends Animal>{
...
}
I'd like all child classes to have a common method that checks the training status of a trainer on a given animal. So I have in my Trainer definition:
public abstract class Trainer<T extends Animal>{
public double getStatus(Animal a){
...
}
}
so that anyone can query a trainer regardless of it's specific type. However, to make the individual trainers responsible for reporting their own status, I requiring a that the individual trainers implement an internalGetStatus method which I'd then like my getStatus method to call. So what I'm currently doing is:
public abstract class Trainer<T extends Animal>{
protected abstract internalGetStatus(T theAnimal);
public double getStatus(Animal a){
return internalGetStatus((T) a);
}
}
The problem is, of course, return internalGetStatus((T) a); involves an unchecked type cast which throws up a warning I'd like to avoid.
Is there a way to do that?
Because of some design limitations beyond my control, when the status is being queried for a particular animal, it is provided as an object of the parent class Animal and not the specific animal type that the trainer is created for.

It depends on how the classes are used. However, you didn't say much about that. Let's start with the following code:
abstract class Animal { }
final class Lion extends Animal { }
abstract class Trainer<T extends Animal> {
public abstract double getStatus(T animal);
}
final class LionTrainer extends Trainer<Lion> {
public double getStatus(Lion lion) {
return 4.2;
}
}
You mentioned that the call-side of the getStatus method doesn't know the animal type. That means he isn't using Trainer<Lion>, but either the raw type, Trainer<Animal> or a wildcard type. Let's go through these three cases.
Raw Type: The type parameter doesn't matter for raw types. You can invoke the method getStatus with Animal. This works as long as the sub-types of Trainer and Animal match. Otherwise, you will see a ClassCastException at runtime.
void raw(Trainer trainer, Animal animal) {
trainer.getStatus(animal);
}
Trainer<Animal>: Obviously, you can invoke the method getStatus of an Trainer<Animal> with an Animal. It similar to the above case. The static type system doesn't ware, and at runtime you will see an exception, if the types don't match.
void base(Trainer<Animal> trainer, Animal animal) {
trainer.getStatus(animal);
}
Note that you can pass a LionTrainer to this method (with cast), because at runtime there is no difference between Trainer<Animal> and Trainer<Lion>.
Wildcard Type: Well, that does not work on the caller-side - without casting the Trainer<?> to something else. The ? stands for an unknown sub-type of Animal (including the base class itself).
void wildcard(Trainer<?> trainer, Animal animal) {
trainer.getStatus(animal); // ERROR
}
The result is, if the framework uses either the raw type or the base type, then there shouldn't be a problem. Otherwise it's simpler to add the cast to your code, suppress the warning with an annotation, and document why you have decided to go that way.

Related

The behavior of the "this" keyword in abstract classes and generics

Consider the following classes
Reinventing the wheel as far as serialization goes, I know:
abstract class AnimalSerializer<E extends Animal> {
/**
* The type E (which extends Animal) is important here.
* I want to be able to write data that is specific to a subclass of an animal.
*/
abstract void writeAnimal(E animal);
abstract Animal readAnimal();
}
abstract class Animal {
AnimalSerializer<? extends Animal> serializer;
Animal(AnimalSerializer<? extends Animal> speciesSerializer) {
serializer = speciesSerializer;
}
void writeAnimalToFile() {
// This line fails to compile
serializer.writeAnimal(this);
}
}
These classes demonstrate the use of this pattern:
class DogSerializer extends AnimalSerializer<Dog> {
#Override
void writeAnimal(Dog animal) {
// Write the stuff that is specific to the dog
// ...
}
#Override
Animal readAnimal() {
// Read the stuff specific to the dog, instantiate it, and cast it as an animal.
// ...
return null;
}
}
class Dog extends Animal {
String dogTag = "Data specific to dog.";
Dog() {
super(new DogSerializer());
}
}
My question pertains to the line that failed to compile (serializer.writeAnimal(this)). I had to pull up the language specification for the first time to learn more about the this keyword, but I think the issue is that the "this" keyword is of the type Animal, and the bounded wildcard generic <? extends Animal> only supports types that are subclasses of Animal, not the Animal type itself.
I would argue that the compiler should know that the type of the this keyword must be an object that extends Animal, given that Animal cannot be instantiated, and the this keyword is only applicable to objects that have been.
Is there a reason why the compiler cannot know this? My guess is that there is a case that explains why the this keyword cannot be guaranteed to be a subclass of Animal.
Furthermore, is this pattern fundamentally flawed as a result?
Your serializer generic type is ? extends Animal. Your this type is Animal, which also can be considered as ? extends Animal. But these two ? are different types. There is no constraint to let the compiler know they are same type.
For example, I write a Cat class
class Cat extends Animal {
Cat(){
super(new DogSerializer()); // this is ok for your generic
}
}
This is why the compiler gives you an error.
In addition to what Dean said:
You can make Animal generic: Animal<S extends Animal>
Then the serializer becomes AnimalSerializer<S> serializer. Now it references a "known" subclass of Animal.
And the extends becomes Dog extends Animal<Dog>. Unfortunately, there is no way to prevent Cat extends Animal<Dog>.
You still have to cast in the offending line: serializer.writeAnimal((S)this);
But now it is possible to do that because you know what class to cast to.
This is the downside of ? in generics.

Why do I need cast in this case?

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

overload method with same generic parameter?

I know that I can't do this:
public abstract class DTODomainTransformer<T, S> {
public abstract S transform(T);
public abstract T transform(S);
}
Because I get the compiler complaint:
Method transform(T) has the same erasure transform(Object) as another method in type Transformer<T,S>
I understand that is because both T and S could be extending same class. So doing this way i can tell him "No, they are not the same, so take it easy"
public interface Transformer<T extends AbstractDTO , S extends AbstractDomain> {
public abstract S transform(T object);
public abstract T transform(S object);
}
Then, my question is, is there any way to tell the compiler that T and S extend from different classes without telling which ones in concrete? I mean, in this last case, I've specified which classes had to be T and S (extending respectively). But what if I want it more generic and not specify them? I'd like to tell the compiler, "Hey, compiler, T and S are not the same! They are different classes. I don't know exactly which classes they are, but I'm sure that they are different".
There's not an obvious way. (Although you can build one, as I show below.)
This overload rule is due to a limitation of how the supertype (in this case, interface) that declares the overloads gets translated (by erasure) to bytecode.
If there's a generic parameter declared T, a method that uses T in its signature will have bytecode generated as the upper bound of T, for example
class Generic<T> {
void work(T t) {}
}
will get erased to
class Generic {
void work(Object t) {}
}
and
class Generic<T extends Number> {
void work(T t) {}
}
will get erased to
class Generic {
void work(Number t) {}
}
This is how the bounded example works, because the overloads erase differently.
public interface Transformer {
public abstract AbstractDomain transform(AbstractDTO object);
public abstract AbstractDTO transform(AbstractDomain object);
}
But without specific bounds, what erased bytecode should be generated for the overloaded methods?
So your T and S being different on the subtype is not what's important. What is important is the known declared bounds which get translated to erased bytecode for the supertype class.
A possible solution could use marker interfaces.
interface TransformT {}
interface TransformS {}
interface Transformable extends TransformT, TransformS {}
interface Transformer<T extends TransformT, S extends TransformS>
T transform(S s);
S transform(T t);
}
abstract class AbstractDTO implements Transformable {}
abstract class AbstractDomain implements Transformable {}
new SomeTransformerImpl<AbstractDTO, AbstractDomain>()
But I don't necessarily recommend doing this. It seems elaborate to me, although interesting. It depends on how complicated the actual class hierarchy is.
What Louis suggested in the comments is much simpler: give the methods different names.

Java inheritance; passing a subclass to an abstract method of a superclass

Sorry for the title, couldn't come up with anything clearer.
I have the following structure:
public interface Vehicle {...}
public class Car implements Vehicle {...}
then:
public abstract class Fixer {
...
abstract void fix(Vehicle vehicle);
...
}
and would like to have:
public class CarFixer extends Fixer {
void fix(Car car) {...}
}
but this doesn't work. Eclipse says: The type CarFixer must implement the inherited abstract method Fixer.fix(Vehicle). Any idea how can I solve this?
You can use Generics to solve this:
public abstract class Fixer<T extends Vehicle> {
abstract void fix(T vehicle);
}
public class CarFixer extends Fixer<Car> {
void fix(Car car) {...}
}
The problem with your original version is that the fix method allows any type of vehicle, but your implementing class allows only cars. Consider this code:
Fixer fixer = new CarFixer();
fixer.fix(new Bike()); // <-- boom, `ClassCastException`, Bike is a vehicle but not a car
You've met the humble home of generics.
Generics provide kind of 'wildcard' type where a class or method can specify that 'we don't really care what type it is, we just need -a- type'.
Generics allow a super class to enforce a specific type in a child class instead of allowing any class that extends a certain class.
This means that you're ultimately enforcing a new highest allowed super-class as the parameter (i.e. Vehicle is no longer the most basic allowable type you can pass to fix(); it's now whatever the subclass says it is, so long as that arbitrary type extends Vehicle).
Common places for generics are container classes (i.e. List, Map, and Set) where the container doesn't really care about what type it tracks, but rather focuses on actually tracking and managing those instances.
Generics consist of one or more type placeholders (in Java, E and T are commonly used but the name doesn't really matter; they usually follow the normal type naming conventions) that are used in place of a specific class or super class.
In your code, you want subclasses to implement methods given their exact relevant types (i.e. a CarFixer would take Cars, a JetpackFixer would take Jetpacks) but you want to enforce that these types extend Vehicle.
In order to enforce this, you have to tell the Fixer class exactly what your subclass wants.
public abstract class Fixer <E extends Vehicle>
{
abstract void fix(E vehicle);
}
Your subclass then extends Fixer, filling in E with the type it wants.
public class CarFixer extends Fixer<Car>
{
#Override
void fix(Car vehicle)
{
// ...
}
}

Overriding a method with Generic Parameters in Java?

I have an abstract Class Monitor.java which is subclassed by a Class EmailMonitor.java.
The method:
public abstract List<? extends MonitorAccount> performMonitor(List<? extends MonitorAccount> accounts)
is defined in Monitor.java and must be overridden in EmailMonitor.java.
I currently have the method overridden in EmailMonitor.java as follows:
#Override
public List<EmailAccount> performMonitor(List<EmailAccount> emailAccounts) {
//...unrelated logic
return emailAccounts;
}
However, this produces the compile time error:
Name clash: The method performMonitor(List<EmailAccount>) of type EmailMonitor has the same erasure as performMonitor(Lis<? extends MonitorAccount> emailAccounts) of type Monitor but does not override it
EmailAccount is a subclass of MonitorAccount, so (in my mind at least) overriding it in this way makes perfect sense. Seeing as the compiler is not happy with my logic though, How should I go about this correctly while still keeping my compile time checks to make sure that all calls to EmailMonitor.performMonitor() receive Lists of EmailAccount rather than some other type of MonitorAccount?
No, it's not overriding it properly. Overriding means you should be able to cope with any valid input to the base class. Consider what would happen if a client did this:
Monitor x = new EmailMonitor();
List<NonEmailAccount> nonEmailAccounts = ...;
x.performMonitor(nonEmailAccounts);
There's nothing in there which should give a compile-time error given your description - but it's clearly wrong.
It sounds to me like Monitor should be generic in the type of account it can monitor, so your EmailMonitor should extend Monitor<EmailAccount>. So:
public abtract class Monitor<T extends MonitorAccount>
{
...
public abstract List<? extends T> performMonitor(
List<? extends T> accounts);
}
public class EmailMonitor extends Monitor<EmailAccount>
{
#Override
public abstract List<? extends EmailAccount> performMonitor(
List<? extends EmailAccount> accounts)
{
// Code goes here
}
}
You might want to think carefully about the generics in the performMonitor call though - what's the return value meant to signify?
Here is my own solution. I suspect this is the same thing Jon Skeet was trying to get at... without the typo (see my comment in reply to his answer).
the Monitor.java class:
public abstract class Monitor <T extends MonitorAccount> {
...
public abstract List<T> performMonitor(List<T> accounts);
..
}
EmailMonitor.java
public class EmailMonitor extends Monitor<EmailAccount> {
...
public List<EmailAccount> performMonitor(List<EmailAccount> emailAccounts) {
..//logic...logic...logic
return emailAccounts;
}
...
}
In this configuration, EmailMonitor.performMonitor() will always check at compile time that it receives a list of EmailAccount rather than any of my other types FTPAccount, DBAccount, etc... It's much cleaner than the alternative, which would have been receiving/sending a raw list and then having to coerce it the required type resulting in potential runtime type casting exceptions.

Categories