我有这个超类Creature
和它的子类Monster
。现在我遇到了这样的问题,即在未初始化的情况下引用final变量。
public class Creature {
private int protection;
public Creature(int protection) {
setProtection(protection);
}
public void setProtection(int p) {
if(!canHaveAsProtection(p))
throw new Exception();
this.protection = p;
}
public boolean canHaveAsProtection(int p) {
return p>0;
}
}
和子类:
public class Monster extends Creature {
private final int maxProtection;
public Monster(int protection) {
super(protection);
this.maxProtection = protection;
}
@Override
public boolean canHaveAsProtection(int p) {
return p>0 && p<maxProtection
}
}
可以看到,当我初始化一个新的Monster
时,它将用super(protection)
调用Creature
的构造函数。在Creature
的构造函数中,调用方法canHaveAsProtection(p)
,该方法通过动态绑定获取Monster
中覆盖的方法。然而,这个被覆盖的版本使用了尚未初始化的最终变量maxProtection
…我怎么解决这个问题?
几点:
- 只有Monster关心最大值,所以只有它应该知道这个概念
- 所有生物必须有一个保护> 0
- 不要将范围检查延迟到单独的方法
- 上界和下界检查不需要在同一个地方
- 使用Decorator Pattern来解决问题
public class Creature {
private int protection;
protected Creature() {
}
public Creature(int protection) {
setProtection(protection);
}
public void setProtection(int p) {
if (p < 0)
throw new IllegalArgumentException();
this.protection = p;
}
}
public class Monster extends Creature {
private final int maxProtection;
private Monster(int protection) {
this.maxProtection = protection;
setProtection(protection);
}
@Override
public void setProtection(int p) {
if (protection > maxProtection)
throw new IllegalArgumentException();
super.setProtection(p);;
}
public static Monster create(int protection) {
Monster monster = new Monster(protection);
monster.validate();
return monster;
}
}
您还没有展示validate()
方法的死亡,但是如果它只需要用于保护检查,我将删除它和静态工厂方法,并将Monster的构造函数设置为公共
Monster
在您发布的代码中没有扩展Creature
。
如果有,我认为Monster
没有理由有final变量。Creature
应该有final变量,Monster
应该直接访问它。如果需要保护的最大值验证,在我看来,所有Creature
实例都应该有它。
从Monster
推到Creature
,你就没事了。在Creature
构造函数中应该有两个参数:protection
和maxProtection
。如果maxProtection < 0
或protection
不在0..maxProtection
范围内,则抛出IllegalArgumentException
这与您的初始化链有关。
子类必须在父类初始化之前被完全初始化。
当前,它看起来是这样的…
怪物->生物->生物# setProtection -> # canHaveAsProtection的怪物…
中maxprotection没有初始化,因为Creature构造函数没有返回。
一个解决方案是以某种方式延迟初始化,也许在构造函数可以调用
在构造函数中调用公共方法确实是一种不好的做法,特别是当您希望将其子类化时。
你的类应该是实体的,并且独立地初始化它的状态。我认为你应该在构造函数中显式设置protection
变量:
public Creature(int protection) {
this.protection = protection;
}
如果你真的想在构造函数中验证你的参数,那么提取公共功能到私有方法中:
public Creature(int protection) {
Assert.isTrue(isProtectionValid(p));
this.protection = protection;
}
private static boolean isProtectionValid(int p) {
return p > 0;
}
public boolean canHaveAsProtection(int p) {
return isProtectionValid(p);
}
鉴于其他答案中提到的原因,请:
public Creature(int protection) {
this.protection = protection;
}
public Monster(int protection) {
super(protection + 1);
this.maxProtection = protection;
}
由于< maxProtection
,该更正+ 1
似乎是您预期的逻辑。
您是否可以重写设置保护,先设置最大设置,然后调用超设置保护。
@override
public void setProtection(int p) {
this.maxProtection = p;
super.setProtection(p);
}
不应该在构造函数中调用公共方法。相反,您可以将构造函数修饰符更改为protected/private,并使用调用验证的工厂方法:
public class Creature {
private int protection;
protected Creature(int protection) {
this.protection = protection;
}
public void setProtection(int protection) {
if (!canHaveAsProtection(protection))
throw new IllegalArgumentException();
this.protection = protection;
}
public boolean canHaveAsProtection(int protection) {
return protection > 0;
}
protected void validate() {
if (!canHaveAsProtection(this.protection))
throw new IllegalArgumentException();
}
public static Creature create(int protection) {
Creature creature = new Creature(protection);
creature.validate();
return creature;
}
}
And Monster
:
public class Monster extends Creature {
private final int maxProtection;
private Monster(int protection) {
super(protection);
this.maxProtection = protection;
}
@Override
public boolean canHaveAsProtection(int p) {
// I changed '<' to '<=' because '<' wouldn't work anyway
return p > 0 && p <= maxProtection;
}
public static Monster create(int protection) {
Monster monster = new Monster(protection);
monster.validate();
return monster;
}
}