英文:
Understanding Java Enum Methods Implementation through an Example
问题
我正在为学校的一个项目工作,与一些人合作,其中一些人提交了一些我很难理解的代码。项目的基础是创建一个包含歌曲、专辑和播放列表的音乐库。特别是这些播放列表,是歌曲的arraylist
,需要不同的排序方式,因此他使用了比较器来进行排序。他是使用枚举实现的,从我理解的角度来看,枚举用于表示不同的实例。
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 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<Song> comparator
。从我的角度来看,它似乎每次都会返回私有的comparator
对象,而不会真正改变Comparable
接口的排序方法。对于解析所有这些内容的任何帮助都将不胜感激。
英文:
So, I'm working on a project right now for school with a few people and one of them has committed some code that I'm really having difficulty wrapping my head around. The basis of the project is creating a music library with songs, albums, and playlists. These playlists, in particular, are arraylists
of songs need different ways of sorting and thus he's implemented comparator for sorting. He did so using enums, which I understand from the perspective of just instances to represent items. Like
public enum Suit {
SPADES, CLUBS, HEARTS, DIAMONDS
}
to represent different suits of a card. I also have learned you can declare methods alongside enums, which is what it looks like he did. Here is the attribute declaration area and the constructor:
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();
}
}
I can vaguely follow what's going on here as such: We start with the constructor, which calls aMethod.getComparator()
, with aMethod
being the enum instance, and then aMethod.getComparator()
returns the this.comparator
object, which itself is declared three lines above as a private Comparator<Song> comparator
. From my perspective, it looks like ithis will return the private comparator
object every time and not actually change the sorting method of the Comparable
interface. Any help parsing all of this would be greatly appreciated.
答案1
得分: 1
你的分析是正确的。这个类看起来很奇怪。有一些突出的点:
- 为什么 Playlist 是 Songs 的比较器?允许使用方法对播放列表进行排序可能会更有意义,而不是在构造时传递。
- 提供的方法对播放列表中歌曲的顺序没有影响。
- 方法枚举可能不应该是私有的。
也许值得重新审视项目中各组件的范围。
- 什么是播放列表?如果歌曲顺序改变了,它是否是不同的播放列表?
- 播放歌曲的方式应该由播放列表决定吗?
英文:
Your analysis is correct. This class seems strange. Some points which stand out:
- Why is Playlist a Comparator of Songs? It may make more sense to allow the playlist to be sorted using a Method instead of passing on construction.
- The Method provided has no impact on the order of Songs in the Playlist.
- The Method enum probably should not be private.
It may be worth revisiting the scope of the components in the project.
- What is a Playlist? Is it a different Playlist if the Song order has changed?
- Should it be up to the Playlist to decide how to play the songs in the playlist?
答案2
得分: 0
只看枚举定义部分。
枚举定义定义了三个实际的枚举:SortByTitle
、SortByLength
和 SortByArtist
- 这些就是该枚举的 SPADE, HEARTS, DIAMONDS, CLUBS;
。对于每个值,它们都使用非零长度的构造函数进行初始化,传递的对象是一个比较器的自定义实现,但现在暂时不要考虑这些细节。
然后枚举值的枚举(嘿)就到此为止。为什么?因为分号。
但枚举定义还没有结束;接下来我们有 private Comparator<Song> comparator;
。
注意,每个枚举值都有其自己的这个字段的副本。枚举的每个值本身就是枚举所代表的 'class' 的一个实例。这里的关键是,该字段保存了不同的比较器,比如 SortByArtist
、SortByLength
等的比较器都是不同的。
因此,Method.SortByArtist.getComparator();
返回的是该字段在 Method
'class' 的实例中的值(枚举本质上是类,有高度限制的构造;每个值只有一个实例,这里只有三个实例)。这与 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 中使用概念 '匿名内部类' 更简洁地编写这段代码。这在你创建一个类并且只打算使用这个定义一次,然后再也不使用它的情况下适用。假设 Comparator<String> c = ...;
这一行是整个代码库中唯一会提到 StringByLengthComparator
的地方。那么可以这样写:
Comparator<String> c = new Conmparator<String>() {
public int compare(String a, String b) {
return a.length() - b.length();
}
};
看起来有点奇怪,但意思是__完全相同__的。唯一的区别是,这个仍然存在的类不再叫做 StringByLengthComparator
,而是一个你不需要担心且永远不能使用的随机名称。但没关系 - 毕竟,我们只会在这一个地方使用这个东西。
Java lambda 表达式。
注意,你甚至可以使用 lambda 表达式让这个更短:
Comparator<String> c = (a, b) -> a.length() - b.length();
仍然表示__完全相同的意思__(嗯,你得到的对象不再具有存在性,除非你做一些非常愚蠢的事情,比如依赖于它的对象标识哈希码,或者在比较器本身上进行锁定,这些都是很疯狂的事情。所以如果你不做任何疯狂的事情,就是一样的)。
这就是这段代码在做的事情。与首先定义三个实现了 Comparable<Song>
的类,然后将 new SongsByLengthComparer()
作为第一个参数的表达式没有什么不同。
英文:
Look only at the enum definition.
The enum definition defines 3 actual enums: SortByTitle
, SortByLength
, and SortByArtist
- those are your SPADE, HEARTS, DIAMONDS, CLUBS;
of this enum. For each value, they are initialized with a non-zero-length constructor, and the object passed is a custom impl of a comparator, but forget all that for now.
The enumeration (heh) of enum values then ends. Why? Because semicolon.
But the enum definition doesn't end yet; then we get private Comparator<Song> comparator;
.
Note that each individual enum value gets its own copy of this field. Each value of an enum is itself an instance of the 'class' that the enum represents. And the key point here is that this field holds different comparators for SortByArtist
, SortByLength
, etc.
Therefore, Method.SortByArtist.getComparator();
returns the value of that field for the instance of the Method
'class' (enums are basically classes, with highly limited construction; only one instance per value, so 3 instances here, ever). Which is different from the value of that field for the SortByLength
instance.
The rest is just anonymous inner classes.
This is valid java, I think it should be fairly obvious to tell, right?
class StringByLengthComparator implements Comparator<String> {
public int compare(String a, String b) {
return a.length() - b.length();
}
}
...
Comparator<String> c = new StringByLengthComparator();
but we can write that with less characters in java, using the concept 'anonymous inner classes'. This works when you make a class and then intent to use this definition exactly once, and then never use it again. Let's say this 'Comparator<String> c = ...;' line is the only place in the entire code base that you're ever going to mention StringByLengthComparator
by name. Then:
Comparator<String> c = new Conmparator<String>() {
public int compare(String a, String b) {
return a.length() - b.length();
}
};
Looks funky, but it means the exact same thing. The one difference is that this class, which still exists, is not named StringByLengthComparator
, but it gets a random name you needn't worry about and cannot ever use. That's okay though - after all, this was the only place we were ever gonna use this thing.
Java lambdas.
Note that you can make this even shorter, using lambda syntax:
Comparator<String> c = (a, b) -> a.length() - b.length();
still means the same thing (well, the object you get no longer has presence, which only matters if you do very silly things, such as relying on its object identity hashcode, or lock on the comparator itself, which are all crazy things to do. So if you don't do anything crazy, it's the same thing).
That's what the code is doing. It's no different than first defining 3 classes that each implement Comparable<Song>
, and then passing new SongsByLengthComparer()
as expression as first argument.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论