7

这是我的第一个 SO 问题,我希望它对读者和我自己都足够有用!在过去的两天里,我用谷歌搜索并避开了这个世界。

我有抽象模型和存储类,从中派生出具体模型和存储类:

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

没问题。现在,我希望能够save()直接在Apple实例上调用 a ,将其持久Basket化(无需担心篮子),并在抽象类中实现它。我发现的最好的是:

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

哪个有效,但 IntelliJ 给了我一个关于未经检查的演员表的警告save()。确实,我正在从Food<T,S>to投射T

问题:如何apple.save()以类型安全的方式实现它?

我不希望客户端代码中出现任何通配符,因此更改abstract void setFood(T food);abstract <Z extends Food<T,S>> void setFood(Z food);不是解决方案。(显然我SupressWarnings("unchecked")也在避免)。

我知道Java 泛型,如何在使用类层次结构时避免未经检查的赋值警告?泛型演员问题,以及get-put 原则,但我仍然无法理解它。

提前致谢!

4

1 回答 1

6

我似乎相当有问题的设计在食物及其储存之间存在相互依赖关系。单向依赖将大大简化泛型:

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

但是,如果您坚持相互依赖,则可以不使用强制转换,如下所示:

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);
}
于 2013-05-22T11:46:26.400 回答