如果我需要可序列化,我应该使用具体的List(例如ArrayList)还是(Serializable)List?

6

我们在办公室中讨论,无法理解哪种方法更好

我有一个类(SomeClass),其中有一些方法可以接收Serializable对象。签名如下:

public void someMethod(Serializable serializableObject){
    ...
}

我需要从另一个类调用这个方法,但是我应该提供一些列表作为实际参数。有两种不同的方法

1. 可序列化

private SomeClass someClass;

public void doSomething() {
    List<String> al = new ArrayList<String>();

    al.add("text");

    someClass.someMethod((Serializable)al);
}

2. ArrayList

private SomeClass someClass;

public void doSomething() {
    ArrayList<String> al = new ArrayList<String>();

    al.add("text");

    someClass.someMethod(al);
}
  1. 第一个例子的好处在于它遵循了Java最佳实践,即:对于引用类型,使用接口而不是具体实现,并且任何程序员在阅读该源代码时都将理解我们不需要特殊的ArrayList行为。我们只需要它的可序列化行为,通过将其强制转换为Serializable接口来添加这种行为。程序员可以简单地将此列表的当前实现更改为其他可序列化实现(例如LinkedList),而不会对此元素产生任何副作用,因为我们使用List作为其引用类型。

  2. 第二个例子的好处在于,我们将ArrayList作为既有List行为又有Serializable行为的类进行引用。因此,如果有人查看此代码并尝试将ArrayList更改为List,则会收到编译时错误,这将减少程序员理解正在发生的事情所需的时间。

更新:我们无法更改someMethod签名。它来自第三方公司,并且我们不仅将其用于可序列化列表,还将其用于字符串、整数和其他一些可序列化对象。

6个回答

2

最好定义ArrayList,因为它结合了两个接口-List + Serializable。你需要在一个地方同时使用它们。

虽然没有那么重要,但是对于返回类型,使用接口应该更加严格,而对于局部变量,则不太严格。


2

当你只需要接口提供的方法时,应该使用接口(在大多数情况下是这样)。然而,如果你需要多个接口,可以使用泛型,但最简单的方法是使用具体类型。


1

我会更改someMethod的签名,以便它反映出调用者需要什么:

public class SomeClass {

    public <T extends List<? extends Serializable> & Serializable> void someMethod(T t) {

    }

    public static void main(String[] args) {
        SomeClass test = new SomeClass();
        test.someMethod(new ArrayList<String>());   //Works
        test.someMethod(new ArrayList<Image>());   //Compile time error, Image is not Serializable
        List<String> l = null;
        test.someMethod(l); //Compile time error
    }
}

someMethod的签名现在表示您必须使用一个List来调用它,并且该列表是Serializable的,其中包含的元素也是Serializable


这可能是一个不错的解决方案,但我们没有机会更改 SomeClass.someMethod() 的签名,因为该方法由第三方库提供,并且它应该接受所有类型的可序列化对象,而不仅仅是可序列化列表。我们的问题只是:在我们必须将 List 的实例传递给此类方法时,哪种方法更好。 - agav
啊,好的。看起来我那时没有完全理解你的问题 :) 在这种情况下,我会使用具体类。 - Kaj
这种方法的缺陷在于,现在调用者必须提供可序列化的列表(例如ArrayList),这意味着调用者的签名必须接受相同类型(例如ArrayList)。这可能会影响许多方法/类,这将非常糟糕,因为它不必要地限制了可以在这些方法中使用的List类型。 - JimN

1
在这种情况下,我只会使用List,不必担心编译器无法保证您的对象是可序列化的(如果您在其他地方做得正确,它很可能已经是可序列化的了)。
请注意,接受Serializable参数的以下类型方法提供了一种虚假的安全感,因为编译器永远无法保证需要序列化的整个对象图实际上是可序列化的。
public void write(Serializable s);

考虑一个包含非可序列化对象的ArrayList(可序列化)。签名可能只是这样的:
public void write(Object o);

然后您就不必担心所有多余的转换。

另外要考虑的是,尽管您无法更改正在使用的API的签名,但可以非常容易地创建具有不同签名的包装器API。


0

你不能做(1),因为你不能随意更改List实现类型,这就是这样做的整个想法。你只能使用实现Serializable接口的List实现。所以你最好在代码中表达出来。


0

通常情况下,选择1是正确的做法。但在这种情况下,我的意见是将其声明为ArrayList<>。这样可以避免强制转换,并确保其他人无法更改List的实现方式,使其不再可序列化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接