0

我正在实现自定义对象的优先级队列。自定义对象类如下所示,是否建议将其实现为通用类,其中 T 只是与 messageId 关联的有效负载?

public class PriQMsgPayload <T> implements Comparable<PriQMsgPayload<T>>{


    private Integer mMsgNum;
    private T ExtraDataForMsg = null;

    public PriQMsgPayload(T extra, PluginConfig.PriQMessage msg)
    {
        mMsgNum= msg;
        ExtraDataForMsg = extra;
    }

    @Override
    public int compareTo(PriQMsgPayload<T> another) {
        return mMsgNum.compareTo(another.mMsgNum);
    }
}

我的疑问:在下面提到的情况下,我首先插入专用于 Integer 的 P1,当我插入另一个专用于 MyClass 的对象 P2 时,比较器不会混淆吗?

PriQMsgPayload<Integer> P1 = new PriQMsgPayload<Integer>(2, 1);
PriQMsgPayload<MyClass> P2 = new PriQMsgPayload<MyClass>(new MyClass(), 2);
4

3 回答 3

1

我认为没有任何意义。当您尝试将对象放入队列时,问题就出现了 - 队列没有完全成熟的类型,它允许它包含这两个!

考虑:

PriorityQueue queue; // works, but is raw
PriorityQueue<PriQMsgPayload> queue; // works, but is partially raw
PriorityQueue<PriQMsgPayload<Object>> queue; // won't accept either object

您可以将它们放入这种类型的队列中(我最初说您不能,但@newacct 好心纠正了我)

PriorityQueue<PriQMsgPayload<?>> queue; // won't accept either object

但这涉及丢弃有关您拥有什么样的额外数据的类型信息。

允许您添加两个对象的队列类型都不会保留额外数据的类型;这意味着当您再次取出对象时,您将丢失所有类型信息。没有办法编写安全地将对象提取为 aPriQMsgPayload<Integer>而不是 a 的代码PriQMsgPayload<MyClass>

于 2012-10-09T08:03:07.327 回答
1

好吧,理想情况下,您不应该将 aPriQMsgPayload<Integer>与 a进行比较PriQMsgPayload<String>——因为 aPriQMsgPayload<Integer>实现了Comparable<PriQMsgPayload<Integer>>,因此它的compareTo方法需要 a PriQMsgPayload<Integer>,所以它不期望 a PriQMsgPayload<String>

在编译时和运行时尝试执行此操作时会发生什么,取决于您的优先级队列类是如何声明的,以及它是如何工作的。您没有告诉我们您正在使用哪种优先级队列实现。

假设您有一个通用的优先级队列类,声明如下:

class MyPriorityQueue<E extends Comparable<? super E>> { ... }

即它要求其类型参数与自身相当。那么就不可能声明一个可以同时插入PriQMsgPayload<Integer>和插入的优先级队列PriQMsgPayload<String>;因为 egMyPriorityQueue<PriQMsgPayload<?>>不会被允许,因为PriQMsgPayload<?>不满足界限。

如果您使用java.util.PriorityQueue,则问题是PriorityQueue该类(以及TreeSetandTreeMap等)不是类型安全的。您可以创建一个new PriorityQueue<Object>(),然后将任何类型的对象插入其中(完全不可比较的对象),并且它不会在编译时捕获任何内容,仅在运行时失败(或者它可能碰巧工作,具体取决于对象的类型)。

在这种情况下,运行时会发生什么?好吧,在类型擦除之后,在运行时,该类等价于:

public class PriQMsgPayload implements Comparable {
    // ....
    public int compareTo(Object another) {
        return compareTo((PriQMsgPayload)another);
    }
    public int compareTo(PriQMsgPayload another) {
        return mMsgNum.compareTo(another.mMsgNum);
    }
}

它只是比较整数,因此在这种情况下,在运行时,它会正常工作。

为什么PriorityQueue类型不安全?好吧,如果您查看该类,它被设计为以两种方式之一使用:您可以提供一个 custom Comparator,或者它将使用对象的“自然排序”。如果是后者,您应该提供与它们自己相当的东西,但这在编译时不会以任何方式强制执行。因为您可以将它与适用于任何类型的自定义比较器(方式#1)一起使用,所以它们不能像我们在MyPriorityQueue上面所做的那样对类的类型参数设置任何界限。

实际上,有一种方法可以使PriorityQueue类型安全,并且仍然能够支持自定义比较器和自然排序模式。只需删除使用自然排序的构造函数(留下使用比较器的构造函数,因为它们是类型安全的),而是用工厂方法替换它们。例如,构造函数

PriorityQueue(int initialCapacity)

可以用工厂方法代替

public static <T extends Comparable<? super T>> PriorityQueue<T> construstUsingNaturalOrdering(int initialCapacity) {
    return new PriorityQueue<T>(initialCapacity, new NaturalOrderingComparator<T>());
}

这样,绑定在工厂方法上的泛型可确保只能为与其自身可比的类型构建自然排序优先级队列。我不知道为什么 Java 库设计者不这样做。

于 2012-10-10T09:53:32.713 回答
0

你到底什么意思?

您不能要求作为参数传递compareTo()(反之亦然);这会给你一个编译错误,因为需要 a ,所以你不能通过 a 。P1P2P1.compareTo()PriQMsgPayload<Integer>PriQMsgPayload<MyClass>

于 2012-10-09T07:54:42.037 回答