将一个通用的超类转换为子类

7

这是我的第一个SO问题,希望对读者和我自己都有足够的用处!我已经在过去两天里通过谷歌和DuckDuckGo搜索了世界各地的相关内容。

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

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

没问题。现在,我希望能够直接在 Apple 实例上调用 save() 方法,将其持久化到它的 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>转换为T
问题是:如何以类型安全的方式实现apple.save()
我不希望客户端代码中出现任何通配符,因此将abstract void setFood(T food);更改为abstract <Z extends Food<T,S>> void setFood(Z food);不是解决方案。(显然也要避免使用SuppressWarnings("unchecked"))。
我知道这篇文章Java Generics, how to avoid unchecked assignment warning when using class hierarchy?,还有这篇文章Generics cast issue,以及这本书The get-put principle,但我仍然无法理解。
提前感谢!

1
你现在尝试的是将一个父类对象转换为子类对象,这应该会引发未经检查的警告,但你需要传递一个扩展了Food的参数,并将其转换为子类对象,如Apple或Banana... 如果我是你,我会这样做 do void save() { getStorage().setFood(this);} 并在每个扩展food的类中覆盖此方法。是的,代码更多了,但设计更加稳固 :/ - Muhammad Bekette
(Thanks for the edit Anders) - wehlutyk
+1 给 @PaulBellora,因为叶子类应该标记为 final。实质上,那个问题的答案与 meriton 的相同。 - wehlutyk
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);
}

太棒了!我刚想到类似的东西,但是“这个”真是太棒了。 - Muhammad Bekette
我也觉得一般般啦 ;) - Seeta Somagani
很好,谢谢@Muhammad。关于相互依赖,我并不特别依恋它,只是没有看到实现apple.save()的其他方法。如果在几天内出现没有相互依赖的建议,我会将其作为答案接受,否则我将验证此答案为最终答案。 - wehlutyk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接