Java 如何避免从超类中施放?

Java 如何避免从超类中施放?,java,generics,inheritance,design-patterns,casting,Java,Generics,Inheritance,Design Patterns,Casting,我正在开发一个具有不同游戏实体的游戏。它工作得很好,但我想在代码中去掉一些强制转换操作。例如,当我检查一颗子弹是否击中敌人时,我需要根据伤害(子弹属性)投掷两个物体以降低生命值(敌人属性) 我将实体与其对应的类一起存储在地图中。看起来是这样的: Map<Class<? extends Entity>, List<Entity>> entities; Map “有没有办法避免对子弹和敌人施放子弹?” TL;DR:是的,有 长答案 做这件事(我认为是最简单的

我正在开发一个具有不同游戏实体的游戏。它工作得很好,但我想在代码中去掉一些强制转换操作。例如,当我检查一颗子弹是否击中敌人时,我需要根据
伤害
(子弹属性)投掷两个物体以降低
生命值
(敌人属性)

我将实体与其对应的类一起存储在地图中。看起来是这样的:

Map<Class<? extends Entity>, List<Entity>> entities;
Map
“有没有办法避免对子弹和敌人施放子弹?”

TL;DR:是的,有



长答案

做这件事(我认为是最简单的方法)

EDD过程包括RPP(“远程配对编程”)。根据@DavidL在我“驾驶”时所做的深刻观察(见评论),他意识到更简单的建议解决方案的副作用使得他的代码比他最初的问题要求的代码更简单

最初的问题是:“如何避免从超类施放?”

是如此的灵活以至于OP发现了一个机会,可以将他的原始代码减少2整行加上9个不必要的字符(上面注释掉的内容)


“但我必须为所有实体实施这些接口…”

在建议的解决方案中,您必须实现
实体
。您目前无论如何都要这样做。无论采用哪种设计,您都必须实现它

为任何其他实现自动生成的启动程序方法只需在现代IDE中单击一次鼠标即可完成。除非您是在操作系统的TextEdit中编写游戏


在您当前的代码中,您必须实现
Bullet
所需的任何行为以及
敌人
所要求的任何行为。无论您选择哪种设计,您仍然必须实现这些行为。

如果我们能够保证只在
实体
中存储非通用对象,那么我们可以访问器类型安全(虽然我们必须强制转换一次,但我们将证明此强制转换是正确的)。我们将使用的概念非常类似于

警告:实现按类类型对对象进行分组。完全忽略接口类型

我们将把
实体
视为内部数据结构,也就是说,我们不允许将对
实体
的任何引用泄漏到外部。通过此参数集,我们引入了一种内部方法,该方法从映射中获取一个
列表
,并转换为正确的类型:

@SuppressWarnings("unchecked")
private <T extends Entity> List<T> getListCasted(Class<? extends T> type) {
    return (List<T>) entities.getOrDefault(type, Collections.emptyList());
}
请注意,
getAll(…)
现在返回内部
列表的(可变)副本

我们不需要修改方法
添加(…)

现在我们需要证明我们在
getListCasted(…)
中进行的未经检查的强制转换是正确的。如果我们查看方法
add(…)
,我们会看到类类型(键)是列表的容器的类型。因此,我们可以保证在某个
a
list
的键下存储了一个
列表。因此,强制转换是合理的

我们甚至可以使用
地图>
放弃
地图上的边界


您是否计划只存储非泛型
对象的
列表
s?任何适合我的问题。如果泛型是答案,我将使用它。我在您输入后浏览堆栈溢出,发现此().你认为呢?如果你愿意,我已经编辑了你在我的回答评论中提到的第45行和第46行。那么这是否解决了你开始要解决的原始问题:“有没有办法避免对子弹和敌人进行这些投掷操作?”?“或者,您是否对必须使用泛型的实现一成不变?99%的情况下,最简单的设计通常是最“正确”的“。但是我必须为所有实体实现这些接口。子弹不应该是一个可破坏的实体。我刚刚通过browxy看到了你的代码。感谢你花时间写这一切。我不知道你可以在子类中再次实现接口,尽管它已经由超类实现了。·……公牛。”et不应是一个可损坏的实体…-原始代码中的此表达式另有说明:“
enemyHit.health-=bulletHit.damage
”。该
.damage
属性表示
项目符号的状态。从语义上讲,它表示:“
项目符号
可以处于
损坏
状态“.用面向对象的术语来说,这意味着
子弹
是可摧毁的
。这有什么意义吗?子弹的
伤害
属性是一个整数。它定义了子弹的“强度”或它对敌人等其他事物的伤害程度。在这种情况下,可能我的代码不太可读。·……可能是我的。”在这种情况下,代码不是那么可读。。。“-不一定是这样。我就是这么看的。下一个家伙可能会按照你自己的想法来看待它。你不需要这样做,所以你没有深入到你问题中属性的语义细节。但这就是问题所在。在没有这些语义细节的情况下,代码本身就是这样。”正如你发布的,f是开放的,可以解释。而且,你对你的游戏更了解。因此,我提供的解决方案可能会让你更好地了解如何继续进行你自己的设计。这看起来很棒。它并没有消除铸造问题,但它很好地外包了。由于铸造的性质,铸造是无法避免的我接受了施法问题,但它很好地外包了……”所以我认为这意味着,尽管你问:“有没有办法避免子弹和敌人的施法操作?”,但没有施法并不是你真正想要的?你的最高要求是什么
for (Entity bullet : data.getAll(Bullet.class)) {
        for (Entity enemy : data.getAll(Enemy.class)) {
            if (bullet.box.overlaps(enemy.box)) {
                // Bullet hits Enemy
                Bullet bulletHit = (Bullet) bullet;
                Enemy enemyHit = (Enemy) enemy;
                
                enemyHit.health -= bulletHit.damage;
                if (enemyHit.health <= 0) {
                    data.remove(enemyHit);
                }
                data.remove(bulletHit);
                
                break;
            }
        }
    }
...
static void play(Gamer data){ 
     for ( Entity bullet : data.getAll(Bullet.class)) {
        for (Entity enemy : data.getAll(Enemy.class)) {
            if (bullet.getBox( ).overlaps( enemy.getBox( ) ) ) {
                // Bullet hits Enemy
                Damagable bulletHit = bullet;
                Illable enemyHit = enemy;
            
                int health = enemyHit.getHealth( );
                int damage = bulletHit.getDamage( );
                enemyHit.setHealth( health -= damage  );
                if ( health <= 0 ) {
                    data.remove(enemy);
                }
                data.remove(bullet);
            
                break;
            }
        }
    }       
}
...
...
/*Damagable bulletHit = bullet;
Illable enemyHit = enemy;*/
            
int health = enemy/*Hit*/.getHealth();
int damage = bullet/*Hit*/.getDamage();
enemy/*Hit*/.setHealth(health -= damage);
...
@SuppressWarnings("unchecked")
private <T extends Entity> List<T> getListCasted(Class<? extends T> type) {
    return (List<T>) entities.getOrDefault(type, Collections.emptyList());
}
private <T extends Entity> List<T> getAll(Class<? extends T> type) {
    return new ArrayList<>(getListCasted(type));
}

public void remove(Entity entity) {
    getListCasted(entity.getClass()).remove(entity);
}