1

I have two solutions to an idea of having an enum that forms a library of comparators:

public enum SongComparator {

    BY_TITLE(Comparator.comparing(Song::getTitle)),
    BY_ARTIST(Comparator.comparing(Song::getArtist)),
    BY_DURATION(Comparator.comparing(Song::getDuration));

    private Comparator<Song> comp;

    private SongComparator(Comparator<Song> comp) {
        this.comp = comp;
    }

    public Comparator<Song> get() {
        return comp;
    }
}

and...

public enum SongComparator2 implements Comparator<Song> {

    BY_TITLE {
        public int compare(Song s1, Song s2) {
            return s1.getTitle().compareTo(s2.getTitle());
        };
    },
    BY_ARTIST {
        public int compare(Song s1, Song s2) {
            return s1.getArtist().compareTo(s2.getArtist());
        };
    },
    BY_DURATION {
        public int compare(Song s1, Song s2) {
            return Integer.compare(s1.getDuration(), s2.getDuration());
        };
    };

}

If I want to substitute an enum value where a Comparator is expected, in the first solution I have to say SongComparator.BY_TITLE.get(); whereas in the second solution I can just say SongComparator2.BY_TITLE.

The second is better in this sense, however, I don't like having to write public int compare... etc for each enum value and wanted instead to make use of Comparator.comparing as in the first approach. Is there a way to achieve this?

I almost wanted to say something like: BY_TITLE { return Comparator.comparing(Song::getTitle); };

Holger
  • 285,553
  • 42
  • 434
  • 765
Tranquility
  • 3,061
  • 5
  • 23
  • 37
  • 2
    http://stackoverflow.com/q/23361418/2711488 Note that the example in that question shows you how to avoid having to call `get()` in the delegation variant. – Holger Jan 21 '16 at 18:06
  • Do you really need an enum? Why not make these 3 comparators constants of a class, rather than enum instances? – JB Nizet Jan 21 '16 at 19:12

1 Answers1

5

What's wrong with:

public enum SongComparator implements Comparator<Song> {

    BY_TITLE(Comparator.comparing(Song::getTitle)),
    BY_ARTIST(Comparator.comparing(Song::getArtist)),
    BY_DURATION(Comparator.comparing(Song::getDuration));

    private Comparator<Song> comp;

    private SongComparator(Comparator<Song> comp) {
        this.comp = comp;
    }

    public int compare(Song s1, Song s2) {
        return comp.compare(s1, s2);
    };

}
Brian Goetz
  • 90,105
  • 23
  • 150
  • 161
  • Ah yes, that will do the trick! Just to double check - if this implementation were to be passed to a sort(...) method, e.g. I passed SongComparator.BY_TITLE. I assume the key extractor function (Comparator.comparing) will only be invoked once, to initialise the enum's field, and not during every callback that the sort would make to the overridden compare method? Instead, I assume all that's happening is a single step of delegation from the enum's overridden compare method to that held by the composed Comparator? – Tranquility Jan 22 '16 at 11:23
  • 1
    Enums are just fancy ways of declaring classes. Once all the instance constructors are run, you're just left with an instance of the enum, which is an ordinary object. – Brian Goetz Jan 22 '16 at 15:22