在泛型类中声明静态泛型变量

16
我读过一个信息,说在泛型类中不能声明静态变量/方法,但我真的不知道如何解决我的问题或绕过它,所以我请求您的指导。
我的需求是一个通用的“索引”,所有核心类都将扩展它。我正在创建一个游戏引擎,例如,我将有不同的游戏状态,它们都扩展了State,而State又扩展了Nexus<State>。我想要静态头和尾,这样我就可以保留所有游戏状态的链接列表,因为它们在创建时都添加到该列表中。
另一个例子是,我将有不同的游戏对象,它们都扩展了GameObject,而GameObject又扩展了Nexus<GameObject>
这是名为Nexus的索引:
public abstract class Nexus<T> 
{

    private static T head = null;
    private static T tail = null;

    private T next = null;
    private static int num = 0;

    protected Nexus() { this.Add( (T)this ); }

    public T Add( T obj )
    {

        ((Nexus)obj).next = null;
        if( num++ == 0 ) head = tail = obj;
        else             tail = ( tail.next = obj );

        return obj;

    }

}

如果有其他解决方案或变通方法,我会全神贯注!

2
根据您的静态字段名称,几乎可以确定在这里不应该使用静态字段。无论是否使用泛型,这都是正确的。但是,您从哪里读到应始终避免在泛型类中使用静态成员?我从未听说过这一点,而且我强烈反对这种说法。 - Kirk Woll
3
没有一个人说你不能在泛型类中使用静态变量,编译器、编译错误信息或异常也没有这样说。 - user207421
好的..!好的,抱歉我的意思是我不能在一个泛型类中拥有静态的GENERIC变量(因为我在private static T head = null;处得到了一个错误)。 谢谢,现在我更清楚地理解为什么我不能做我正在做的事情。然而,我仍然无法想出一种实现我想要做的事情的方法,即拥有一个通用工厂类,该类在所有使用工厂类的类之间使用链表,但链表仅在相同类型名称的类之间“共享”。 - Tanax
@Tanax 你需要更清楚地解释一下。目前听起来有点自相矛盾。要么你是在链接所有的类,要么每个类都有一个单独的列表。你是想要链接每个类的所有对象吗? - user207421
我会尽力的,呵呵。我想要将所有Nexus<State>对象链接起来,这样每个继承Nexus<State>的类都会被放置在它们自己的链接列表中,而每个继承Nexus<GameObject>的类则会被放置在另一个链接列表中。这样解释清楚了吗? - Tanax
显示剩余5条评论
4个回答

12

Java的泛型与C#的泛型有很大的不同。 存在类型擦除,因此你不能像C#那样说Nexus<T>.aStaticPublicField。 你只能说Nexus.aStaticPublicField。 由于没有实例,所以无法知道泛型类型,因此无法拥有类型为T的静态字段。


是的,这正是我在这个网站上所有其他相关问题中发现的。你知道实现我所需的另一种方法或解决方法吗? - Tanax
@Tanax:你可以保留一个以类型为键的(头部,尾部)对字典。 - Ben Voigt
@Petar,Nexus<String>.aStaticPublicField有什么问题吗? - Pacerier

10
尝试这种方法:定义一个protected abstract方法,由子类实现并返回一个static对象,代表它们自己的类。
可能会有一些逻辑问题等等,但答案的基本内容在这里(即这个可以编译): 编辑:现在转而委托给HeadAndTail
/** <T> A subclass of Nexus */
abstract class Nexus<T extends Nexus<T>> { // This syntax lets you confine T to a subclass of Nexus
    private T next;

    protected Nexus() {
        this.add((T) this);
    }

    public T add(T obj) {
        // Delegate to HeadAndTail
        return getHeadAndTail().add(obj);
    }

    /** @return a static for the class */
    protected abstract HeadAndTail<T> getHeadAndTail();
}

/** Bundled into one Object for simplicity of API */
class HeadAndTail<T extends Nexus<T>> {
    T head = null;
    T tail = null;
    int num = 0;

    public T add(T obj) {
        obj.next = null;
        if (num++ == 0)
            head = tail = obj;
        else
            tail = tail.next = obj;

        return obj;
    }
}

class ConcreteNexus extends Nexus<ConcreteNexus> {
    // This is the static object all instances will return from the method
    private static HeadAndTail<ConcreteNexus> headAndTail = new HeadAndTail<ConcreteNexus>();

    protected HeadAndTail<ConcreteNexus> getHeadAndTail() {
        return headAndTail; // return the static
    }
}

看起来很棒!我会尝试实现它并查看是否有效:D - Tanax
当我只有一个类型时,这段代码运行得非常好(我用了两个不同的游戏状态类进行测试,它们都扩展了扩展Nexus<State>的状态)。但是当我添加了一个扩展了Nexus<GameObject>的GameObject时,我遇到了一个异常:Exception in thread "main" java.lang.NullPointerException at Nexus.Add(Nexus.java:35) at Nexus.<init>(Nexus.java:16) at GameObject.<init>(GameObject.java:17) at SpaceShip.<init>(Game.java:47) at Game.main(Game.java:67)。我真的不明白为什么会出现这种情况。在Nexus中,35是:GetHeadAndTail().tail = ( GetHeadAndTail().tail.next = obj ); - Tanax
哦,我知道!Num是静态的并且在所有Nexus类之间共享,因此对于该类型名称,head和tail永远不会被初始化,因为num不为0。 - Tanax
@Tanax:让我们把“num”移动到小助手类中,并让“Nexus”委托它-请参见更新的代码。 - Bohemian
是的,那正是我做的 :) 除了我没有把添加方法移到那里,有没有什么理由更喜欢把它放在那里,而不是将其放在Nexus内部并执行if( getHeadAndTail().num++ == 0 ) - Tanax
显示剩余5条评论

1

我相当确定该教程早于泛型。 - Ben Voigt
你能不能只写成 AbstractClass.staticMethod<type>() - Tom Busby
1
你好Tom!我认为问题不在于抽象(abstract),而是泛型(<T>)的使用。 - Tanax
1
事实上,看了你的代码后,我不明白为什么head和tail要是静态的。你似乎正在实现一个单向链表,在这种情况下,head和tail不应该是静态的,否则你只能拥有类的一个实例,否则任何新的实例都会覆盖其他所有实例的head和tail。 - Tom Busby
((Nexus)obj).next = null; 这是一行非常奇怪的代码,您确定要将传入的对象转换为Nexus对象吗? - Tom Busby
嗯,我想要实现的是为所有的Nexus<something>和另一个静态头部和尾部为所有的Nexus<somethingelse>创建一个静态头部和尾部,这样我创建的所有Nexus<State>都会有自己的链表,而我创建的所有Nexus<GameObject>也会有自己的链表。 - Tanax

0
一组静态字段并不是实现这个目标的最佳方式。我不太理解你的要求,但更好的方法似乎是更改此类的构造函数签名以传递全局索引对象。
也就是说,不要使用这种方式:
protected Nexus() { this.Add( (T)this ); }

...你可以尝试这样做:

protected Nexus(GameStateIndex<T> index) { index.Add(this); }

这样做可以很好地区分跟踪游戏状态和跟踪所有游戏状态索引的职责(请参见"单一职责原则")。它还明确表明,创建状态对象需要正确依赖索引。


抱歉,也许我在原帖中解释得不够清楚,但我想要做的是创建一个扩展了State(其中包含各种方法)的StateOne。State又扩展了Nexus<State>,因此当我创建StateOne实例时,它将自动添加到Nexus<State>中的内部静态链接列表中。我还想能够创建一个扩展了GameObject(也包含各种方法)的SpaceShip,它又扩展了Nexus<GameObject>,因此当我创建SpaceShip实例时,它将被添加到Nexus<GameObject>列表中。 - Tanax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接