在初始化之前引用final变量的Java



我有这个超类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构造函数中应该有两个参数:protectionmaxProtection。如果maxProtection < 0protection不在0..maxProtection范围内,则抛出IllegalArgumentException

这与您的初始化链有关。

子类必须在父类初始化之前被完全初始化。

当前,它看起来是这样的…

怪物->生物->生物# setProtection -> # canHaveAsProtection的怪物…

中maxprotection没有初始化,因为Creature构造函数没有返回。

一个解决方案是以某种方式延迟初始化,也许在构造函数可以调用

的init方法中。

在构造函数中调用公共方法确实是一种不好的做法,特别是当您希望将其子类化时。

你的类应该是实体的,并且独立地初始化它的状态。我认为你应该在构造函数中显式设置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;
    }
}

相关内容

  • 没有找到相关文章

最新更新