【问题标题】:Understanding Java Enum Methods Implementation through an Example通过示例了解 Java 枚举方法的实现
【发布时间】:2021-01-06 10:21:12
【问题描述】:

所以,我现在正在与几个人一起为学校做一个项目,其中一个人提交了一些我真的很难理解的代码。该项目的基础是创建一个包含歌曲、专辑和播放列表的音乐库。这些播放列表尤其是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() 返回this.comparator 对象,它本身就是将上面的三行声明为private Comparator&lt;Song&gt; comparator。从我的角度来看,看起来 ithis 每次都会返回私有 comparator 对象,而实际上并没有改变 Comparable 接口的排序方法。任何解析所有这些的帮助将不胜感激。

【问题讨论】:

  • 您还需要检查代码中实际使用Method 枚举的位置。上面的代码都是关于以更智能的方式定义多种排序策略。 :) 但是,确切的问题是什么或您没有正确理解哪一行?
  • 除了你的问题:(1)为什么aName = new String(pName);而不是简单的aName = pName;?字符串是不可变的,那么这里期望什么优势? (2) 为什么class Playlistimplements Comparator&lt;Song&gt;??播放列表的实例是否应该像 List&lt;Song&gt; songs = ...; songs.sort(playlistInstance) 一样使用?
  • @JudeNiroshan 我想这是关于如何将输入作为定义的枚举之一提供给构造函数将改变排序度量。现在看起来,传递的任何枚举都决定了如何使用Collection.sort 来比较这些值。但我在当前的实现中没有看到它实际上是如何改变comparator 对象的。它似乎每次都返回相同的private Comparator&lt;Song&gt; comparator
  • @Pshemo 1. 我不知道为什么会这样 2. 播放列表在其arraylist 属性中填充了歌曲,因此决定我们需要一种组织方式说playList。 playList 数组列表只会存在于 Playlist 对象中。
  • 我们需要一种组织播放列表的方法”,但这就是为什么你有 private Comparator&lt;Song&gt; aComparator;... 所以 implements Comparator&lt;Song&gt; 很可能是多余的/错误的。跨度>

标签: java enums comparator implements


【解决方案1】:

你的分析是正确的。这门课看起来很奇怪。突出的几点:

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

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

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

【讨论】:

  • 1.1.我不完全确定,但我们现在研究和关注这个项目的主题是封装,所以它可能是限制客户端访问播放列表的排序方式/内部方法。 1.2.好吧,我就是这么想的。我们应该做的是返回与排序方法/枚举相关的comparator。 1.3 我再次认为这样做是为了遵循封装原则以防止直接访问 2.1 播放列表是song 对象的集合,可以以不同的方式排序。 2.2 播放方式从arraylist.get(0)到last。
【解决方案2】:

只看枚举定义。

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

枚举值的枚举(heh)然后结束。为什么?因为分号。

但是枚举定义还没有结束;然后我们得到private Comparator&lt;Song&gt; comparator;

请注意,每个单独的枚举值都有自己的该字段副本。枚举的每个值本身就是枚举所代表的“类”的一个实例。而这里的关键是这个字段为SortByArtistSortByLength等保存了不同的比较器。

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

剩下的只是匿名内部类。

这是有效的java,我认为应该很明显,对吧?

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

...

Comparator<String> c = new StringByLengthComparator();

但我们可以使用“匿名内部类”的概念在 java 中使用更少的字符来编写它。当你创建一个类然后打算只使用这个定义一次,然后再也不使用它时,这很有效。假设这个'比较器 c = ...;' line 是整个代码库中唯一一个你会提到StringByLengthComparator 的地方。那么:

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

看起来很时髦,但它意味着完全相同的东西。一个区别是这个仍然存在的类没有被命名为StringByLengthComparator,但是它有一个随机的名字,你不必担心也永远不能使用。不过没关系 - 毕竟,这是我们唯一会使用这个东西的地方。

Java lambdas

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

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

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

这就是代码正在做的事情。这与首先​​定义 3 个类,每个类都实现 Comparable&lt;Song&gt;,然后将 new SongsByLengthComparer() 作为表达式作为第一个参数传递没有什么不同。

【讨论】:

  • 啊,我明白了。让我搞砸的是枚举声明:SortByArtist(new Comparator&lt;Song&gt;() {}), 我不知道为什么SortByArtist 括号内有这么多东西(),通常保留用于传递参数。那么他是否在SortByArtist输入参数中初始化了新的comparator
  • 值得一提的是,当你有一个enum的比较器策略时,也可以让enum类型本身实现Comparator,这比委托给匿名内部更简洁类(但不如 lambda 表达式简洁)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
  • 2014-12-04
  • 1970-01-01
相关资源
最近更新 更多