سؤال

Is there a way to create a class which implements Comparator and allows only a singleton instance to be created, or failing that only one instance per parameterized type?

I have tried a couple of mechanisms (both using a private constructor and then a getInstance method) but neither was valid syntax. So I'm wondering if type erasure means that it's impossible to rely on a single instance of a generic Comparator class.

The aim is to avoid having dozens of instances of the class, because all will behave identically and so a sole instance seems desirable. Is it possible? If not, what is the suggested way to avoid wild duplication of Comparator instances?

EDIT: This question seems to have caused confusion about what I'm trying to achieve, so I'll paste one of my broken attempts here. The code below is an attempt to create a Map which holds only a single instance of the Comparator for each type T. This is valid syntax, but I'm unable to call the getInstance method because I have no way to get a Class<T> instance in the client code.

final class IntervalComparator<T extends Comparable<T>> implements
        Comparator<Interval<T>> {

    // Private constructor. Use the getInstance method instead.
    private IntervalComparator() {
    }

    // Map to hold one instance for each type.
    private static Map<Class, IntervalComparator> cacheMap = new HashMap<>();

    // The only method which returns an instance of this Comparator.
    public static <K extends Comparable<K>> IntervalComparator<K>
            getInstance(Class<K> type) {
        IntervalComparator<K> soleInstance = cacheMap.get(type);
        if(soleInstance == null) {
            soleInstance = new IntervalComparator<>();
            cacheMap.put(type, soleInstance);
        }
        return soleInstance;
    }

    @Override
    public int compare(Interval<T> o1, Interval<T> o2) {
        // ... Comparison code goes here ...
    }
}

The client code looks like this:

private Set<Interval<T>> intervalSet;
private IntervalUnion(IntervalUnion.Builder<T> builder) {
    this.intervalSet = new TreeSet<>(new IntervalComparator<T>());
    // ... add Interval<T> objects into intervalSet ...
}

At the moment it is creating a new IntervalComparator<T> object each time it is called, and I don't think there's any way to pass Class<T> to the getInstance method.

هل كانت مفيدة؟

المحلول

I believe you can use one single comparator object for your purposes.

I assume that your comparator implementation IntervalComparator is stateless (in the sense that it has no fields to store anything). In this case you can always use the very same code to do the comparison.

final class IntervalComparator<T extends Comparable<T>> implements
    Comparator<Interval<T>> {

    // Private constructor. Use the getInstance method instead.
    private IntervalComparator() {
    }

    private static IntervalComparator INSTANCE = new IntervalComparator();

    @SuppressWarnings("unchecked")
    public static <K extends Comparable<K>> IntervalComparator<K> getInstance() {
        return (IntervalComparator<K>)INSTANCE;
    }

    @Override
    public int compare(Interval<T> o1, Interval<T> o2) {
        // ... Comparison code goes here ...
    }
}

Your use case would then be as follows:

private Set<Interval<T>> intervalSet;

private IntervalUnion() {
this.intervalSet = 
    new TreeSet<Interval<T>>(
      IntervalComparator.<T>getInstance());
// ... add Interval<T> objects into intervalSet ...
}

Note that there is an annotation @SuppressWarnings("unchecked") at the polymorphic getInstance() method. This is to surpress the compiler warning that the cast occurring in the method cannot be checked due to type erasure in Java generics.

By the way, there is a prominent persuasive precedent for such an implementation. The method java.util.Collections.emptyList() is similarly implemented:

@SuppressWarnings("unchecked")
public static final List EMPTY_LIST = new EmptyList<>();
// ...

@SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}

(cf. http://www.docjar.com/html/api/java/util/Collections.java.html#3161)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top