我有一个带有某个类(Piece)的ArrayList,我想从中选择带有子类(Miner和Bomb)的项目。但是,当我选择一些项目时,它将使用超类(Piece)的方法,而不是它的子类。
由于ArrayList<Pieces>
将包含多个片段的子类对象,因此无法为ArrayList定义更具体的类。是否可以从ArrayList中选择一些元素并仍然使用子类中的方法?
(我发现找到解决这个简单问题的方法有点困难。我搜索了覆盖,arrayList,方法以及其他一些东西,但是找不到它。如果它太明显了,我和#39;我想知道你在哪里找到它。)
班级主要:
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<Piece> pieceList = new ArrayList<>();
pieceList.add(new Miner());
pieceList.add(new Bomb());
// create new classes to test canDefeat
if (new Miner().canDefeat(new Bomb())) {
System.out.println("Yes! This line will be printed.");
}
System.out.println();
// test canDefeat with pieces from the pieceList
if (pieceList.get(0).canDefeat(pieceList.get(1))) {
System.out.println("No, this line will not be printed");
}
}
}
Class Piece:
public abstract class Piece {
public boolean canDefeat(Piece opponent) {
return false;
}
}
班级矿工:
public class Miner extends Piece {
public boolean canDefeat(Bomb opponent) {
return true;
}
}
类炸弹:
public class Bomb extends Piece {
}
答案 0 :(得分:2)
为什么这很容易:
您的Miner
无法覆盖canDefeat(Piece)
,它会添加一个带有签名canDefeat(Bomb)
的新方法。
在代码pieceList.get(0).canDefeat(pieceList.get(1))
中,所有编译器必须使用的是Piece
类型,因此它将其解析为Piece#canDefeat(Piece)
。
做什么而不是更棘手。
我会建议哪种类型的作品可以击败其他类型的作品并不是每件作品的属性;一方面,如果你改变规则,这将很快变得脆弱。
相反,我会考虑以下替代方案:
也许每一件都有一个强度等级,强度越高意味着这件作品就能击败一件力量越来越小的作品。 (以下示例。)
您可以拥有一个BattleLogic
类,可以比较各种类型的碎片以确定结果,这可以使用上述强度,也可以使用instanceof
等类型。通常当你到达instanceof
时,你需要停下来思考你正在做什么,以及它是否真的是适合这项工作的工具(通常不是),但它很可能是此工作的正确工具。 BattleLogic
类的优点是没有任何部分知道其他部分;相反,你有一件事情可以理解整个游戏以及棋子在战斗时的排名。 (以下示例。)
如果你希望每个Piece
知道它可以击败的其他类型的作品,你可以这样做,但我认为它会导致一个难以维持的混乱。想到两种方式:
列出可以击败的类型。 (以下示例。)
使用反射来实现您原本想要做的事情。 (以下示例。)
以下是代码中的强度建议:
import java.util.ArrayList;
class Piece {
private int strength;
public Piece(int strength) {
this.strength = strength;
}
public int getStrength() {
return this.strength;
}
public boolean canDefeat(Piece opponent) {
return this.getStrength() > opponent.getStrength();
}
}
class Miner extends Piece {
public Miner() {
super(10);
}
}
class Bomb extends Piece {
public Bomb() {
super(5);
}
}
public class Main {
public static void main(String[] args) {
ArrayList<Piece> pieceList = new ArrayList<>();
pieceList.add(new Miner());
pieceList.add(new Bomb());
// create new classes to test canDefeat
if (new Miner().canDefeat(new Bomb())) {
System.out.println("Yes! This line will be printed.");
}
System.out.println();
// test canDefeat with pieces from the pieceList
if (pieceList.get(0).canDefeat(pieceList.get(1))) {
System.out.println("Yes! This line will also be printed");
}
}
}
代码中的BattleLogic
建议:
import java.util.ArrayList;
abstract class Piece {
}
class Miner extends Piece {
}
class Bomb extends Piece {
}
class BattleLogic {
/**
* Does a battle between the two pieces.
*
* @param a The first piece
* @param b The second piece
* @return 0 for a tie, a negative number if a beats b, or a positive number if b beats a
*/
public static int battle(Piece a, Piece b) {
if (a instanceof Miner && b instanceof Bomb) {
return -1;
}
// ...further tests here...
return 0;
}
}
public class Main {
public static void main(String[] args) {
ArrayList<Piece> pieceList = new ArrayList<>();
pieceList.add(new Miner());
pieceList.add(new Bomb());
// create new classes to test canDefeat
if (BattleLogic.battle(new Miner(), new Bomb()) < 0) {
System.out.println("Yes! This line will be printed.");
}
System.out.println();
// test canDefeat with pieces from the pieceList
if (BattleLogic.battle(pieceList.get(0), pieceList.get(1)) < 0) {
System.out.println("Yes! This line will also be printed");
}
}
}
这是第三个建议,让每种类型的作品知道它可以击败哪些碎片,以防万一。尽管如此,我并不是这样建议的;我认为对彼此了解这些内容将是一个维护问题:
// Not really recommended
import java.util.List;
import java.util.ArrayList;
abstract class Piece {
private List<Class> iDefeat;
public Piece(Class... defeats) {
this.iDefeat = new ArrayList<Class>();
for (Class c : defeats) {
this.iDefeat.add(c);
}
}
public boolean canDefeat(Piece piece) {
return this.iDefeat.contains(piece.getClass());
}
}
class Bomb extends Piece {
}
class Miner extends Piece {
public Miner() {
super(Bomb.class);
}
}
public class Main {
public static void main(String[] args) {
ArrayList<Piece> pieceList = new ArrayList<>();
pieceList.add(new Miner());
pieceList.add(new Bomb());
// create new classes to test canDefeat
if (new Miner().canDefeat(new Bomb())) {
System.out.println("Yes! This line will be printed.");
}
System.out.println();
// test canDefeat with pieces from the pieceList
if (pieceList.get(0).canDefeat(pieceList.get(1))) {
System.out.println("Yes! This line will also be printed");
}
}
}
或者,如果您要获得数百万件并且内存存在问题,您可以将列表设为静态;这只是一个草图。
最后,使用反射的方式:
// Not reall recommended
import java.lang.reflect.Method;
import java.util.ArrayList;
class Piece {
public Piece() {
}
private static Method getMethodOrNull(Class cls, String name, Class... paramTypes) {
try {
return cls.getMethod(name, paramTypes);
}
catch (NoSuchMethodException nsme) {
return null;
}
}
public boolean canDefeat(Piece opponent) {
try {
Class thisClass = this.getClass();
Class oppClass = opponent.getClass();
Method m = null;
while (oppClass != Piece.class && (m = getMethodOrNull(thisClass, "canDefeat", oppClass)) == null) {
oppClass = oppClass.getSuperclass();
}
if (m != null) {
return (boolean)m.invoke(this, opponent);
}
return false;
}
catch (Exception iae) {
throw new RuntimeException("Can't access method", iae);
}
}
}
class Miner extends Piece {
public boolean canDefeat(Bomb b) {
return true;
}
}
class Bomb extends Piece {
}
public class Main {
public static void main(String[] args) {
ArrayList<Piece> pieceList = new ArrayList<>();
pieceList.add(new Miner());
pieceList.add(new Bomb());
// create new classes to test canDefeat
if (new Miner().canDefeat(new Bomb())) {
System.out.println("Yes! This line will be printed.");
}
System.out.println();
// test canDefeat with pieces from the pieceList
if (pieceList.get(0).canDefeat(pieceList.get(1))) {
System.out.println("Yes! This line will also be printed");
}
}
}
答案 1 :(得分:0)
Miner
未覆盖canDefeat
- Piece
方法,因为参数类型已缩小。您可以通过添加
@Override
到您认为在编译时要重写的方法。
此外,在抽象超类中提供默认行为并不是很好的OO风格,而是在可能的情况下将行为转移到具体的子项中,否则您可能会无意识地继承您想要覆盖的行为。在您的情况下,请canDefeat
为摘要,让Bomb
使用Piece
中当前存在的行为来实现它。
答案 2 :(得分:0)
您只需要更改canDefeat
的签名以接受泛型类型,并确保以正确的方式执行覆盖,两个签名始终相同
片段类
public abstract class Piece {
public boolean canDefeat(Piece opponent) {
return false;
}
}
Miner 类
public class Miner extends Piece {
@Override
public boolean canDefeat(Piece opponent) { // Piece not Bomb
return true;
}
}
更新以让
Miner
仅失败Bomb
片段类
public abstract class Piece<T> {
public boolean canDefeat(T opponent) {
return false;
}
}
Miner 类
public class Miner extends Piece<Bomb> {
@Override
public boolean canDefeat(Bomb opponent) { // Defeats Bomb
return true;
}
}
答案 3 :(得分:0)
除了@ T.J的正确答案。 Crowder我建议你也考虑在更加面向数据的方法中实现这一切,而不是面向对象的方法。这看起来像是一个完美的问题,可以在Entity-System框架(如"Artemis")中实现,其中你的Piece是一个具有强度组件的实体,整个逻辑进入BattleSystem。
这种方法适用于改变规则的高度。例如,如果你的失败不仅与力量有关,而且与健康有关,那么很容易在力量组件中添加一个健康参数,甚至可以创建一个额外的健康组件,而战斗系统中的逻辑可以处理它。 这种方法中更困难的是实体,组件和系统的正确设计以及满足您特定需求的系统间通信。