简体   繁体   English

Java中Generic类的两种不同特化之间的比较

[英]Comparision between two different specialization of the Generic class in Java

I am implementing a priority queue of custom objects. 我正在实现自定义对象的优先级队列。 The custom object class looks like below, would it be recommended to implement it as a generic class, where T simply is a payload associated with the messageId? 自定义对象类如下所示,是否建议将其实现为泛型类,其中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);
    }
}

My doubt: in below mentioned case, I first insert the P1 specialized for Integer and when I insert another object P2 which is specialized for MyClass, would the comparator not get confused? 我怀疑:在下面提到的情况下,我首先插入专门用于Integer的P1,当我插入另一个专门用于MyClass的对象P2时,比较器会不会混淆?

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

I don't think there's any point. 我认为没有任何意义。 The problem comes when you try to put the objects into the queue - there is no fully-cooked type that the queue can have which allows it to contain both of these! 当你试图将对象放入队列时会出现问题 - 队列没有完全煮熟的类型,它允许它包含这两个类型!

Consider: 考虑:

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

You can put them into a queue of this type (i initially said you couldn't, but @newacct kindly corrected me) : 你可以将它们放入这种类型的队列中(我最初说你不能,但是@newacct很好地纠正了我)

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

But that involves throwing away the type information about what kind of extra data you have. 但这涉及丢弃有关您拥有哪种额外数据的类型信息。

None of the queue types which let you add both objects preserve the type of the extra data; 任何允许您添加两个对象的队列类型都不会保留额外数据的类型; that means that when you got the objects out again, you would have lost all type information. 这意味着当你再次拿出对象时,你会丢失所有类型的信息。 There's no way to write code that safely extracts the object as a PriQMsgPayload<Integer> rather than a PriQMsgPayload<MyClass> . 没有办法编写安全地将对象作为PriQMsgPayload<Integer>而不是PriQMsgPayload<MyClass>提取的代码。

Well, ideally, you shouldn't be able to compare a PriQMsgPayload<Integer> with a PriQMsgPayload<String> -- since a PriQMsgPayload<Integer> implements Comparable<PriQMsgPayload<Integer>> , and thus its compareTo method takes a PriQMsgPayload<Integer> , so it does not expect a PriQMsgPayload<String> . 好吧,理想情况下,您应该无法将PriQMsgPayload<Integer>PriQMsgPayload<String> - 因为PriQMsgPayload<Integer>实现了Comparable<PriQMsgPayload<Integer>> ,因此其compareTo方法采用PriQMsgPayload<Integer> ,所以它不期望PriQMsgPayload<String>

What happens when you try to do this, both at compile time and at runtime, depends on how your priority queue class is declared, and how it works. 在编译时和运行时尝试执行此操作时会发生什么,取决于您的优先级队列类的声明方式及其工作方式。 You did not tell us what implementation of priority queue you are using. 您没有告诉我们您正在使用的优先级队列的实现。

Suppose you had a generic priority queue class that is declared like this: 假设您有一个通用优先级队列类,声明如下:

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

ie it requires its type parameter is comparable to itself. 即它要求其类型参数与其自身相当。 Then it would be impossible to declare a priority queue that you would be able to insert both PriQMsgPayload<Integer> and PriQMsgPayload<String> into; 那么就不可能声明一个优先级队列,你可以将PriQMsgPayload<Integer>PriQMsgPayload<String>插入到; because eg MyPriorityQueue<PriQMsgPayload<?>> would not be allowed because PriQMsgPayload<?> does not satisfy the bounds. 因为例如MyPriorityQueue<PriQMsgPayload<?>>将不被允许,因为PriQMsgPayload<?>不满足边界。

If you are using java.util.PriorityQueue , the problem is that the PriorityQueue class (along with TreeSet and TreeMap , etc.) are not type-safe. 如果您使用的是java.util.PriorityQueue ,则问题是PriorityQueue类(以及TreeSetTreeMap等)不是类型安全的。 You could create a new PriorityQueue<Object>() , and then insert whatever kinds of objects into it (completely non-comparable ones), and it would not catch anything at compile time, failing only at runtime (or it might happen to work, depending on the types of objects). 您可以创建一个new PriorityQueue<Object>() ,然后将任何类型的对象插入其中(完全不可比较的对象),并且它不会在编译时捕获任何内容,仅在运行时失败(或者它可能正常工作) ,取决于对象的类型)。

What happens at runtime in this case? 在这种情况下运行时会发生什么? Well, after type erasure, at runtime, the class is equivalent to this: 好吧,在类型擦除之后,在运行时,类等同于:

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

It simply compares the integers, and so in this case, at runtime, it will work fine. 它只是比较整数,因此在这种情况下,在运行时,它将正常工作。

Why is PriorityQueue not type safe? 为什么PriorityQueue不安全? Well, if you look at the class, it's designed to be used in one of two ways: you can provide a custom Comparator , or it will use the "natural ordering" of the objects. 好吧,如果你看一下这个类,它的设计可以用两种方式之一:你可以提供一个自定义Comparator ,或者它将使用对象的“自然顺序”。 If the latter, you are supposed to provide things that are comparable to themselves, but this is not enforced at compile time in any way. 如果是后者,你应该提供与自己相当的东西,但这不会在编译时以任何方式强制执行。 Because you can use it with a custom comparator (way #1), which works for any type, they cannot put any bounds on the type parameter of the class, like we did with MyPriorityQueue above. 因为您可以将它与自定义比较器(方式#1)一起使用,它适用于任何类型,它们不能在类的类型参数上放置任何边界,就像我们上面的MyPriorityQueue

Actually, there is a way to make PriorityQueue type-safe, and still be able to support both custom comparators and natural ordering modes. 实际上,有一种方法可以使PriorityQueue类型安全,并且仍然能够支持自定义比较器和自然排序模式。 Simply remove the constructors that use natural ordering (leaving the ones that use a comparator, since those are type-safe), and instead replace them with factory methods. 只需删除使用自然排序的构造函数(保留使用比较器的构造函数,因为它们是类型安全的),而是用工厂方法替换它们。 For example, the constructor 例如,构造函数

PriorityQueue(int initialCapacity)

can be replaced by the factory method 可以用工厂方法代替

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

That way, the generics bound on the factory method ensures that natural ordering priority queues can only be constructed for types that are comparable to themselves. 这样,工厂方法绑定的泛型确保自然排序优先级队列只能为与自身相当的类型构建。 I don't know why the Java library designers didn't do it this way. 我不知道为什么Java库设计者没有这样做。

What exactly do you mean? 你到底什么意思?

You cannot call compareTo() on P1 passing P2 as an argument (or vice versa); 你不能在P1上调用compareTo()传递P2作为参数(反之亦然); that would give you a compile error, because P1.compareTo() expects a PriQMsgPayload<Integer> , so you cannot pass a PriQMsgPayload<MyClass> . 这会给你一个编译错误,因为P1.compareTo()需要一个PriQMsgPayload<Integer> ,所以你不能传递PriQMsgPayload<MyClass>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM