通过示例了解 Java 枚举方法实现

Understanding Java Enum Methods Implementation through an Example

所以,我现在正在和几个人一起为学校做一个项目,其中一个人提交了一些我真的很难理解的代码。该项目的基础是创建一个包含歌曲、专辑和播放列表的音乐库。特别是这些播放列表 arraylists 的歌曲需要不同的排序方式,因此他实现了排序比较器。他使用枚举来做到这一点,我从仅实例的角度理解枚举来表示项目。喜欢

    public enum Suit {
       SPADES, CLUBS, HEARTS, DIAMONDS
    }

代表一张牌的不同花色。我还了解到您可以在枚举旁边声明方法,他看起来就是这么做的。这里是属性声明区和构造函数:

 public class Playlist implements Comparator<Song>{
    
        private TotalTime aTotalTime;
        private String aName;
        private ArrayList<Song> playList;
        private Comparator<Song> aComparator;
        private enum Method{
            SortByTitle(new Comparator<Song> () {
    
                @Override
                public int compare(Song o1, Song o2) {
                    // TODO Auto-generated method stub
                    return o2.getTitle().compareTo(o1.getTitle());
                    }
                
            }),
            SortByArtist(new Comparator<Song>() {
    
                @Override
                public int compare(Song o1, Song o2) {
                    // TODO Auto-generated method stub
                    return o2.getExpectedTags().getArtist().compareTo(o1.getExpectedTags().getArtist());
                    }
                
            }),
            SortByLength(new Comparator<Song>() {
    
                @Override
                public int compare(Song o1, Song o2) {
                    // TODO Auto-generated method stub
                    return o2.getTotalSeconds()-o1.getTotalSeconds();
                }
                
            });
            private Comparator<Song> comparator;
            Method(Comparator<Song> pComparator){
                comparator = pComparator;   
            }
            public Comparator<Song> getComparator(){
                return this.comparator;
            }
        }
    
        // constructor that initializes the the playlist.
        public Playlist(String pName,Method aMethod) {
            aName = new String(pName);
            playList = new ArrayList<Song>();
            this.aComparator = aMethod.getComparator();
        }
}

我可以模糊地理解这里发生的事情:我们从调用 aMethod.getComparator() 的构造函数开始,aMethod 是枚举实例,然后是 aMethod.getComparator() return 是 this.comparator 对象,它本身在上面三行中声明为 private Comparator<Song> comparator。从我的角度来看,看起来它每次都会 return 私有 comparator 对象,而不是实际更改 Comparable 接口的排序方法。任何解析所有这些的帮助将不胜感激。

您的分析是正确的。这 class 似乎很奇怪。突出的几点:

  1. 为什么播放列表是歌曲的比较器?允许使用方法对播放列表进行排序而不是传递构造可能更有意义。
  2. 提供的方法对播放列表中歌曲的顺序没有影响。
  3. 方法枚举可能不应该是私有的。

可能值得重新审视项目中组件的范围。

  1. 什么是播放列表?如果歌曲顺序改变了,是否是不同的播放列表?
  2. 是否应该由播放列表来决定如何播放播放列表中的歌曲?

只看枚举定义。

枚举定义定义了 3 个实际枚举:SortByTitleSortByLengthSortByArtist - 这些是您对该枚举的 SPADE, HEARTS, DIAMONDS, CLUBS;。对于每个值,它们都使用 non-zero-length 构造函数进行初始化,传递的对象是比较器的自定义实现,但暂时忘记所有这些。

枚举值的枚举(呵呵)到此结束。为什么?因为分号.

但是枚举定义还没有结束;然后我们得到 private Comparator<Song> comparator;.

请注意,每个单独的枚举值都有该字段的副本。枚举的每个值本身就是该枚举所代表的 'class' 的一个实例。这里的关键点是该字段包含 SortByArtistSortByLength 等不同的比较器

因此,Method.SortByArtist.getComparator(); returns Method 'class' 实例的那个字段的值(枚举基本上是 classes,高度有限的构造;每个值只有一个实例,所以这里有 3 个实例)。这与 SortByLength 实例的该字段的值不同。

其余只是匿名内classes.

这是有效的java,我想应该很明显了吧?

class StringByLengthComparator implements Comparator<String> {
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
}

...

Comparator<String> c = new StringByLengthComparator();

但是我们可以用 java 中更少的字符来写,使用概念 'anonymous inner classes'。当您创建 class 然后打算只使用此定义一次,然后再也不会使用它时,这会起作用。假设此 'Comparator c = ...;' 行是整个代码库中唯一您要按名称提及 StringByLengthComparator 的地方。那么:

Comparator<String> c = new Conmparator<String>() {
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
};

看起来很古怪,但这意味着 完全一样的东西。一个区别是这个 class,它仍然存在,没有命名为 StringByLengthComparator,但它有一个随机名称,您不必担心并且永远不会使用。不过没关系 - 毕竟,这是我们唯一会用到这个东西的地方。

Java lambdas.

请注意,您可以使用 lambda 语法使此 更短

Comparator<String> c = (a, b) -> a.length() - b.length();

仍然意味着同样的事情(好吧,你得到的对象不再存在,只有当你做了非常愚蠢的事情时才有意义,比如依赖它的对象标识哈希码,或者锁定比较器本身,这些都是所有疯狂的事情都要做。所以如果你不做任何疯狂的事情,那就是一回事。

这就是代码的作用。这与首先​​定义 3 个 class 没有什么不同,每个实现 Comparable<Song>,然后将 new SongsByLengthComparer() 作为表达式作为第一个参数传递。