Java中泛型类的两种不同专业化的比较

时间:2012-10-09 07:41:31

标签: java generics

我正在实现自定义对象的优先级队列。自定义对象类如下所示, 是否建议将其作为泛型类实现,其中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);

3 个答案:

答案 0 :(得分: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

但这涉及丢弃关于您拥有哪种额外数据的类型信息。

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

答案 1 :(得分:1)

嗯,理想情况下,您不应该将PriQMsgPayload<Integer>PriQMsgPayload<String>进行比较 - 因为PriQMsgPayload<Integer>实施Comparable<PriQMsgPayload<Integer>>,因此compareTo {1}}方法需要PriQMsgPayload<Integer>,因此不需要PriQMsgPayload<String>

在编译时和运行时尝试执行此操作时会发生什么情况取决于声明优先级队列类的方式及其工作方式。您没有告诉我们您正在使用的优先级队列的实现方式。

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

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

即。它要求其类型参数与自身相当。那么就不可能声明一个优先级队列,你可以将PriQMsgPayload<Integer>PriQMsgPayload<String>同时插入;因为例如<{1}}不允许MyPriorityQueue<PriQMsgPayload<?>>,因为PriQMsgPayload<?>不满足界限。

如果您使用的是java.util.PriorityQueue,则问题是PriorityQueue类(以及TreeSetTreeMap等)不是类型安全的。您可以创建一个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不安全?好吧,如果你看一下这个课程,它可以用两种方式之一:你可以提供一个自定义Comparator,或者它会使用&#34;自然顺序&#34;的对象。如果是后者,那么假设提供与自身相当的东西,但这不会在编译时以任何方式强制执行。因为您可以将它与自定义比较器(方式#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库设计师没有这样做。

答案 2 :(得分:0)

你到底是什么意思?

您无法在compareTo()P1传递P2作为参数(反之亦然);这会给你一个编译错误,因为P1.compareTo()期望PriQMsgPayload<Integer>,所以你不能传递PriQMsgPayload<MyClass>