试图保持 ArrayList 不可变

Trying to keep ArrayList Immutable

所以我有一个名为 class 的相册,其中包含一个名为 'listOfAllAlbumsCreated' 的静态 ArrayList。

   public class Album {
    private static ArrayList<Album> listOfAllAlbumsCreated= new ArrayList<>();

           public Album(String albumTitle) {
                this.albumTitle = albumTitle;
                listOfAllAlbumsCreated.add(this);

    }


public static ArrayList<Album> getListOfAllAlbumsCreated() {
        return listOfAllAlbumsCreated;
    }// I only want PlayList Class to Access this!

}

基本上,每当创建相册对象时,'listOfAllAlbumsCreated' 都会将该对象添加到其中。然后,我创建了一个 PlayList Class,它应该可以访问所有创建的专辑 - 通过获取 'listOfAllAlbumsCreated' 并为其分配另一个 ArrayList。

 public class PlayList {
    private static ArrayList<Album> allExistingAlbums = Album.getListOfAllAlbumsCreated();

 public PlayList(String playListName) {
        this.playListName = playListName;
    }

}

但是,这允许其他 classes 中的任何人只需调用 Album.getListOfAllAlbumsCreated 并可能更改我显然不想更改的所有已创建相册的内容。那么我怎样才能保证 listOfAllAlbumsCreated 的安全;即只能访问我的播放列表 class?

谢谢!

我认为您在这里混淆了一些概念。

某物 "immutable" 意味着它一旦创建就无法更改。似乎您不希望您的 ArrayList 是不可变的 - 只是保护谁可以访问它。

为了保护谁可以访问您的数据(或调用您的方法),您应该更改其 access modifiers - publicprotectedpackage-private 之一或 private.

截至目前,除了 Album class 之外,没有人可以实际更改 listOfAllAlbumsCreated ArrayList 的内容,因为该变量是私有的,而且似乎没有任何方法改变它的内容。 getListOfAllAlbumsCreated 是 public,这意味着任何 class 都可以调用此方法并查看 ArrayList 中的内容。

如果您只希望 Playlist 能够调用此方法,则需要进行一些重构(除非 Playlist 将是您的包中唯一的其他 class...这不太可能)。但是,Album 和 Playlist 之间似乎没有有意义的关系 - 使 Playlist 成为 Album 的子 class 似乎不是一个合理的解决方案。相反,我会弄清楚 listOfAllAlbumsCreated 是否真的需要在相册 class 中。一个专辑知道所有其他存在的专辑似乎有点奇怪;这可能不是好的设计。我要么将专辑的 ArrayList 存储在另一个 class 中(也许是播放列表 class,因为你想限制访问 only 这个 class),或者创建一个 AlbumList class.

是的,这是可能的。我想到了两个选项。

选项 1:通过访问修饰符限制访问。

Oracle tutorial on access control has a neat table overview of what the different access modifiers do (Table generate with Senseful):

╔═════════════╦═══════╦═════════╦══════════╦═══════╗
║  Modifier   ║ Class ║ Package ║ Subclass ║ World ║
╠═════════════╬═══════╬═════════╬══════════╬═══════╣
║ public      ║ Y     ║ Y       ║ Y        ║ Y     ║
║ protected   ║ Y     ║ Y       ║ Y        ║ N     ║
║ no modifier ║ Y     ║ Y       ║ N        ║ N     ║
║ private     ║ Y     ║ N       ║ N        ║ N     ║
╚═════════════╩═══════╩═════════╩══════════╩═══════╝

根据您的描述,private 修饰符(仅在当前 class 中授予访问权限),no modifier(仅在当前 class 中授予访问权限和 classes 在同一包中)或 protected 修饰符(从当前 class、classes 在同一包中授予访问权限并派生 class es) 可能是您正在寻找的。该示例显示它没有修饰符。

static ArrayList<Album> getListOfAllAlbumsCreated() {
    return listOfAllAlbumsCreated;
}

选项 2:Return 列表的不可修改视图。

return 不可修改的 List throung Collections.unmodifiableList(...) 视图。您的代码将如下所示:

public static ArrayList<Album> getListOfAllAlbumsCreated() {
    return Collections.unmodifiableList(listOfAllAlbumsCreated);
}

关于不可修改列表的两句警告:

  • 调用 getListOfallAlbumsCreated() 后对原始 List 所做的更改不会反映在不可修改的视图中。不可修改视图只是您创建不可修改视图时的列表视图。
  • 虽然 returned List 是不可修改的,但 List 中的条目不是。 Java 没有 const 对象的概念,就像 C++ 一样。要实现真正的不变性,您需要将 Album class 设计为不可变的。将来,我们很可能会有 value types,这将简化不可变对象的实施,并带来一些简洁的性能优势。

两条设计备注:

  • 你所做的基本上是重新创建 Java - 相当于内存泄漏。 Album 的实例不会被垃圾回收,除非您也将其从 listOfAllAlbumsCreated 中删除。这可能是也可能不是您想要的。您可以做的是使用 WeakReferences,以允许对对象进行垃圾回收,前提是该对象仅存在 WeakReferences。您可以将 listOfAllAlbumsCreated 更改为 List<WeakReference<Album>>。在 return 中,这可能会导致某些引用 return null 当您调用 get() 时的情况。您可以尝试通过重写 finalize() 方法来解决此问题,但强烈建议不要这样做。

  • 如果您已经对 Album 进行了某种簿记,则可以实施 Factory Method Pattern or the Builder Pattern in order to prevent creation of duplicates. For a more complete example of this, you may want to take a look at the implementation of Integer.valueOf(int value).

我能想到的唯一方法是让Playlist静态内部class of Album,丢掉getter并访问listOfAllAlbumsCreated内部Playlist一个人:

public class Album {
    private static ArrayList<Album> listOfAllAlbumsCreated= new ArrayList<>();

    public Album(String albumTitle) {
        this.albumTitle = albumTitle;
        listOfAllAlbumsCreated.add(this);
    }

    public static class PlayList {

        public PlayList(String playListName) {
            this.playListName = playListName;
        }

        public someMethod() {
            listOfAllAlbumsCreated.get(0);
        }
    }
}

这可能不是一个好的解决方案,因为在 Album class 中声明 Playlist 确实没有意义,但它确实可以满足您的需求.