3

我有这个超类Creature及其子类Monster。现在我有一个最终变量被引用而没有被初始化的问题。

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

如您所见,当我初始化一个 new 时Monster,它会调用Creaturewith的构造函数super(protection)。在 的构造函数中Creature,调用该方法,该方法canHaveAsProtection(p)通过动态绑定获取Monster. 然而,这个被覆盖的版本使用了maxProtection尚未初始化的最终变量......我该如何解决这个问题?

4

7 回答 7

1

几点:

  • 只有 Monster 关心最大值,所以只有它应该知道这个概念
  • 所有生物的保护必须 > 0
  • 不要将范围检查推迟到单独的方法
  • 上限和下限检查不需要在同一个地方
  • 使用装饰器模式解决问题

将所有这些放在一起,您的代码应如下所示:

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 的构造函数公开。

于 2013-05-23T03:52:23.483 回答
0

这与您的初始化链有关。

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

目前,它看起来像这样......

怪物->生物->生物#setProtection->Monster#canHaveAsProtection ...

其中 maxProtextion 尚未初始化,因为 Creature 构造函数尚未返回。

一种解决方案是推迟初始化,可能是在构造函数可以调用的 init 方法中

于 2013-05-22T20:47:06.113 回答
0

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

你的类应该是可靠的并且独立地初始化它的状态。我认为您应该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);
}
于 2013-05-22T20:50:56.407 回答
0

您不应该在构造函数中调用公共方法。相反,您可以将构造函数修饰符更改为受保护/私有并使用调用验证的工厂方法:

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

并且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;
    }
}
于 2013-05-22T21:42:04.437 回答
0

由于其他答案中提到的原因,请执行以下操作:

public Creature(int protection) {
    this.protection = protection;
}

public Monster(int protection) {
    super(protection + 1);
    this.maxProtection = protection;
}

由于. + 1_< maxProtection

于 2013-05-22T21:13:25.627 回答
0

您如何覆盖 set protection en set the max first en 而不是调用 super set protection 。

    @override
   public void setProtection(int p) {
        this.maxProtection = p;
    super.setProtection(p);
    }
于 2013-05-22T21:14:30.750 回答
0

Monster不会Creature在您发布的代码中扩展。

如果是这样,我认为没有理由Monster拥有最终变量。 Creature应该有最终变量,并且Monster应该简单地访问它。如果需要对保护进行最大值验证,在我看来,所有Creature实例都应该拥有它。

将它从Monsterto向上推Creature,你就没事了。您应该在Creature构造函数中有两个参数:protectionmaxProtection. 抛出IllegalArgumentExceptionifmaxProtection < 0protection超出范围0..maxProtection

于 2013-05-22T20:40:03.933 回答