【问题标题】:Is overriding static field with static block bad practice?用静态块覆盖静态字段是不好的做法吗?
【发布时间】:2015-07-11 09:35:59
【问题描述】:

我想创建数据结构来捕捉以下想法:

在游戏中,我希望有一个通用的 Skill 类,它可以捕获技能 ID、冷却时间、法力消耗等一般信息。

然后我想拥有定义实际交互和行为的特定技能。所以这些都是从基类Skill扩展而来的。

最后,每个玩家都会有这些特定技能的实例,所以我可以查看每个玩家的技能状态,玩家最近是否使用过等等。

所以我有一个抽象超类Skill,它定义了一些所有技能共有的静态变量,然后对于扩展Skill 的每个单独技能,我使用一个静态块来重新分配静态变量。所以我有以下模式:

class A {
    static int x = 0;
}

class B extends A {
    static {
        x = 1;
    }
}

...

// in a method
A b = new B();
System.out.println(b.x);

上面打印出 1,这正是我想要的行为。我唯一的问题是系统抱怨我以非静态方式访问静态变量。但是我当然不能以这种方式访问​​它,因为我只想将技能视为Skill,而不知道它到底是哪个子类。所以我每次这样做时都必须抑制警告,这让我想到这里是否有更好/更整洁的设计模式。

我曾考虑过将相关变量设为非静态变量,但因为它们在特定技能的所有个实例中应该是静态的,所以我觉得它应该是一个静态变量...

【问题讨论】:

  • 如果每个技能子类都有自己的 x 值,那么您应该在每个子类中都有一个静态的 x 字段。照原样,加载 B 类会将 A.x 设置为 1。使您的 x 变量成为最终变量。如果您想以多态方式访问变量值,那么您应该在每个类中都有一个非静态的getX() 方法。它是否总是返回相同的常量值是一个实现细节。
  • 问题是...您不会更改特定技能的所有实例,而是更改所有技能的所有实例,因为您更改了 A 类中的属性(我假设是您的 @ 987654330@班级)。因此,对于扩展 A 的所有类,x1。我倾向于为您的技能使用接口,定义所需的方法并隐藏实现细节。您可以拥有某种public int getX() 而无需关心实现。
  • 这只是一个完整的旁白,但我会将这两件事完全分开:我有一个 SkillInfo 类/枚举,其中包含有关技能的所有通用信息(名称、描述、先决条件等),技能不会扩展这个,每个都有一个实例。您可以创建单独的类来实现技能行为,每个技能对应一个。
  • 好像连代码都不行!好吧,既然Java不允许覆盖静态方法,我想没有办法让这些共享变量成为静态的……

标签: java static subclassing


【解决方案1】:

您通常应该避免这样使用全局状态。如果您确定 x 字段将在基类的 all 子类型的 all 实例之间共享,那么放置此类字段的正确位置可能是基类以外的地方。它可能在其他一些配置对象中。

但是即使使用您当前的配置,它也没有任何意义,因为任何修改静态变量的子类都会使该变量对所有类可见。如果子类Bx 更改为1,然后子类C 将其更改为2,那么B 也可以看到新值。

我认为您在问题中描述的方式,每个子类都应该有自己独立的静态字段。而在抽象基类中,您可以定义一个由每个子类实现的方法,以便访问每个字段:

abstract class A {

    public abstract int getX();
}

class B extends A {
    public static int x = 1;

    public int getX() {
        return x;
    }
}

class C extends A {
    public static int x = 2;

    public int getX() {
        return x;
    }
}

【讨论】:

  • 嗯,看来我应该更广泛地测试它。但是,您将如何对其建模?我认为我的问题是我想声明一个我认为也应该是静态的抽象方法/字段,因为在子类中,值在所有实例中都是恒定的。
  • 没有抽象字段,抽象方法不能是静态的。我认为您在问题中描述的方式,每个子类都应该有自己独立的静态字段。
  • 但是那样我就失去了抽象。例如,我不能遍历Skills 的列表,我知道每个列表都有一个名为foo 的静态变量,但我不知道每个实例到底是什么子类。我也不希望使用反射。
  • 正如其他 cmets 之一所述,您可以在基类中定义一个抽象方法,每个子类都可以实现该方法以返回该静态字段。请参阅我的更新答案。
【解决方案2】:

正如一些答案和 cmets 已经指出的那样,您的方法不会按照您想要的方式工作,因为每个静态块都会更改所有扩展 A 的类的静态变量。

改用接口和实例方法:

public interface A {
    int getX();
}

-

public class B implements A {

    private static final int X = 1;

    @Override
    public int getX() {
        return X;
    }
}

-

A myInstance = new B();
System.out.println(myInstance.getX()); // prints "1"

【讨论】:

  • 但是,由于值在 B 的所有实例中都是恒定的,所以我觉得将它作为 A 知道的静态方法会更优雅......我想这真的不支持Java...
  • 在 B 的所有实例中只会有一个 X 的静态实例。但正如您猜对的那样,没有办法使用 A 中定义的静态字段或方法来做您想做的事。跨度>
猜你喜欢
  • 1970-01-01
  • 2011-07-07
  • 2011-02-14
  • 2010-12-28
  • 2011-09-22
  • 2011-08-13
  • 2018-06-21
  • 1970-01-01
相关资源
最近更新 更多