Question

This is my first SO question, I hope it's useful enough, both for readers and myself! I've googled and ducked the world around with this for the past two days.

I have abstract model and storage classes from which concrete model and storage classes are derived:

abstract class Food {}

abstract class FoodStorage<T extends Food> {
    abstract void setFood(T food);
}

class Apple extends Food {}

class Basket extends FoodStorage<Apple> {
    @Override
    void setFood(Apple apple) {
        // Save that apple to the basket
    }
}

No problem. Now, I would like to be able to call a save() directly on an Apple instance, persisting it to its Basket (without having to bother about the basket), and have that implemented in the abstract classes. The best I've found yet is this:

abstract class Food<T extends Food<T,S>,
        S extends FoodStorage<T,S>> {

    abstract S getStorage();

    void save() {
        getStorage().setFood((T)this); // <---- Unchecked cast warning
    }

}

abstract class FoodStorage<T extends Food<T,S>, S extends FoodStorage<T,S>> {
    abstract void setFood(T food);
}

class Apple extends Food<Apple,Basket> {
    Basket basket = new Basket(); // or Basket.getInstance();

    @Override
    Basket getStorage() {
        return basket;
    }
}

class Basket extends FoodStorage<Apple,Basket> {
    @Override
    void setFood(Apple apple) {
        // Save that apple to the basket
    }
}

Which works, but IntelliJ gives me a warning about the unchecked cast in save(). Indeed, I'm casting from Food<T,S> to T.

Question: how can I implement this apple.save() in a typesafe way?

I don't want any wildcards appearing in the client code, so changing abstract void setFood(T food); to abstract <Z extends Food<T,S>> void setFood(Z food); is not the solution. (Obviously I'm avoiding SupressWarnings("unchecked") also).

I'm aware of Java Generics, how to avoid unchecked assignment warning when using class hierarchy? , of Generics cast issue , and of The get-put principle, but I still can't get my head around it.

Thanks in advance!

Was it helpful?

Solution

I seems rather questionable design to have a mutual dependency between the food and its storage. A unidirectional depedency would simplify the generics greatly:

class Food { ... }
class FoodStorage<F extends Food> {
    void setFood(F f);
}

But if you insist on the mutual dependency, you can do it without a cast as follows:

abstract class Food<F extends Food<F, S>, S extends FoodStorage<F, S>> {
    abstract F getThis();
    abstract S getStorage();

    void save() {
        getStorage().setFood(getThis());
    }
}
abstract class FoodStorage<F extends Food<F, S>, S extends FoodStorage<F, S>> {
    abstract void setFood(F food);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top