生成唯一的自动增量实例ID

3

我一直在尝试为类实例生成唯一的ID,从1开始,像这样:

public abstract class foo {
    static int ID = 0;
    final int id;

    public foo() { this.id = ++ID; }
}

我意识到我有几个需要这种行为的类,因此我尝试创建一个抽象类来继承,但我发现静态字段不能真正被继承。在Java中,有没有办法重复使用这个普遍的行为?
编辑: 我没有说清楚,但我需要每个子类的索引不仅唯一,而且连续。
例如,如果我有bar和baz从foo继承:
bar b1 = new bar();
baz b2 = new baz();
bar b3 = new bar();
应该有,而应该有。


可能是重复的问题:Java-给每个对象实例一个唯一的编号 - user180100
不,通常是根据类来完成的(或者基于类的层次结构)。你不应该创建一个抽象类只因为你需要一个计数器。 - M. Page
@RC. 这不是那个问题的重复。这个问题是关于如何在多个类之间重用(已经理解的)行为。 - chiastic-security
@chiastic-security 我不明白为什么不行,参考 http://ideone.com/Nt8g5k - user180100
@RC。我编辑了我的问题。实际上,你在代码片段中展示的行为就是我在尝试继承抽象类时遇到的问题。我需要每个子类的ID与子类实例连续。 - Ionuț-Claudiu Herciu
3个回答

1
正如其他人所指出的,这可能不是一个好主意。但如果您想这样做,只需在基类构造函数中递增计数器即可。
abstract class foo {
    static int globalId = 0;
    final int id;
    foo() {
        id = globalId;
        ++globalId;
    }
}

如果需要线程安全,请使用AtomicInteger。


如果我只是在基类中递增计数器,那么所有的子类将共享相同的ID生成,使用相同的globalId。这不是我所需要的。 - Ionuț-Claudiu Herciu
是的,所有的类都将共享相同的id生成方式,但每个类都会获得一个唯一的id。globalId是静态的,因此整个程序只有一个。你的问题表明你只需要确保每个实例分配一个唯一的数字,这个方法可以做到。听起来你的要求可能更具体:每个子类都有独特的整数,且对于每个子类,这些整数从0开始,并且没有间隙。这是你所需要的吗? - Oliver Dain
是的,这就是我需要的,每个子类的ID都要连续。对于之前的问题表达不清楚,抱歉。已经修改了问题。 - Ionuț-Claudiu Herciu

1
不完全是!你可以尝试以下几点:
1. 创建一个单一的IDGenerator类,其中包含一个用于为特定类生成新ID的静态方法。generate()方法将接受Class参数,并维护一个将类映射到最近给出标识符的HashMap。这将把所有逻辑放入一个类中,但不会真正简化事情。
2. 在每个想要使用这种行为的类内部创建一个新的IDGenerator类的静态引用。在这种情况下,IDGenerator类不需要Class参数,因为每个使用它的类都有一个实例。其generate()方法将是一个无参方法。同样,我不确定这是否会让事情更简单。
3. 放弃连续ID的要求。这样,您可以拥有一个IDGenerator类,其中包含一个静态的无参generate()方法,该方法只需提供下一个未使用的标识符即可。然后,您的标识符将在应用程序中是唯一的,并且不是类内连续的。
你拥有的是相当简单易懂的并且很精简。 另外需要注意的一点是:你说你希望ID从0开始,但是按照你的写法,它们将会从1开始。你需要使用this.id = ID++而不是this.id = ++ID

这是个错误。实际上我意思是它们应该从1开始。已进行修正。非常感谢您理解我的意思,尽管我表达得不清楚。我真的希望我能给您的回答点赞。 - Ionuț-Claudiu Herciu
@RedCat23,你的声望还不足以进行点赞,但如果这个回答对你有帮助,请点击勾选为采纳答案(这也会给你一些声望积分)。 - chiastic-security

0
一种选择是使用UUID(http://docs.oracle.com/javase/7/docs/api/java/util/UUID.html
UUID.randomUUID();

第二个选择是只使用AtomicInteger来实现这个功能:
    AtomicInteger idGen = new AtomicInteger();
    System.out.println(String.format("First is %d", idGen.getAndIncrement()));
    System.out.println(String.format("Second is %d", idGen.getAndIncrement()));

我不会在这个问题中使用抽象类、继承甚至自定义类。

编辑:这里是一个完整的例子,支持初始代码。我不明白你怎么可能让它更简单:

public static class foo {
    final public int id;

    foo(int id) { this.id = id; }
}

public static class bar extends foo {
    static final AtomicInteger idGen = new AtomicInteger(1);

    public bar() {
        super(idGen.getAndIncrement());
    }
}

public static class baz extends foo {
    static final AtomicInteger idGen = new AtomicInteger(1);

    public baz() {
        super(idGen.getAndIncrement());
    }
}

public static void main(String[] args) {
    bar b1 = new bar();
    baz b2 = new baz();
    bar b3 = new bar();

    System.out.println(String.format("b1.id = %d", b1.id));
    System.out.println(String.format("b2.id = %d", b2.id));
    System.out.println(String.format("b3.id = %d", b3.id));
}

UUIDs太长了,没有必要。使用AtomicInteger听起来很理想,但是我正在使用一个抽象类,因为我需要重用那个行为。 - Ionuț-Claudiu Herciu
如果你需要重用行为,可以重用AtomicInteger实例。继承是一种糟糕且不适当的机制来实现重用。 - clay
此外,当标准JDK已经有一个完全满足你需求的类时,你真的不应该创建一个新的类。 - clay
但是这样我就必须在每个参与的类中重复构造函数的递增代码。 - Ionuț-Claudiu Herciu
是的,但这只是构造函数中的一行代码。我不认为继承的方式更简单。请发布一个简化的示例代码。我假设你会将id传递给父类foo。 - clay
显示剩余3条评论

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