2013-03-02 22 views
4

這是一個brainscratcher。從Callable返回的本地類的序列化

我知道這個實際的代碼在很多層面上都很糟糕。我的問題不是如何這樣做(我知道關於靜態初始化塊),但是爲什麼這個不行,爲了我的理解Java序列化的好處。

爲什麼這項工作

import java.io.*; 
import java.util.*; 

class Main { 

    static Comparator<String> COMPARE_STRING_LENGTH; 
    static { 
     class CompareStringReverse implements Comparator<String>, Serializable { 
      public int compare(String o1, String o2) { 
       return o1.length() - o2.length(); 
      } 
     }; 
     COMPARE_STRING_LENGTH = new CompareStringReverse();  
    } 

    public static void main(String[] args) throws IOException { 
     ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("test.ser")); 
     out.writeObject(new TreeSet<String>(COMPARE_STRING_LENGTH)); 
     out.close(); 
    } 

} 

而這

import java.io.*; 
import java.util.*; 
import java.util.concurrent.Callable; 

class Main { 

    static Comparator<String> COMPARE_STRING_LENGTH = new Callable<Comparator<String>>() { 
     public Comparator<String> call() { 
      class CompareStringReverse implements Comparator<String>, Serializable { 
       public int compare(String o1, String o2) { 
        return o1.length() - o2.length(); 
       } 
      }; 
      return new CompareStringReverse(); 
     } 
    }.call(); 

    public static void main(String[] args) throws IOException { 
     ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("test.ser")); 
     out.writeObject(new TreeSet<String>(COMPARE_STRING_LENGTH)); 
     out.close(); 
    } 

} 

產生

Exception in thread "main" java.io.NotSerializableException: Main$1 

回答

6

那麼 「爲什麼」 要稍微含蓄。即使您的名爲類(CompareStringReverse)實現了Serializable,但它嵌套在匿名類中。因此它是一個內部類,並且隱式引用了封閉匿名類的一個實例。例如,如果運行:

javap -c Main$1$1CompareStringReverse 

你會看到領域:

final Main$1 this$0; 

這是什麼不能被序列化。你仍然可以修復它很容易,但:

interface SerializableCallable<T> extends Serializable, Callable<T> {} 

... 

static Comparator<String> COMPARE_STRING_LENGTH = 
    new SerializableCallable<Comparator<String>>() { 
     ... 
}; 

唯一顯著不同的是,匿名類現在實現Serializable以及Callable<T>。我不想認爲有任何方法可以指定一個匿名類來實現多個接口(即你必須創建額外的接口來組合這兩個接口),但我可能是錯的。

所以現在命名的內部類實現了Serializable,其唯一字段的類型也是如此,所以一切正常。

+0

實際上,我想我錯了這個,因爲我的類不是真正的「匿名」,因爲他們有名稱「CompareStringReverse」,但他們已經**做**實現Serializable。 – 2013-03-02 17:46:52

+0

@PaulDraper:啊,錯過了 - 但它是一個匿名類中的* inner *類,這意味着有一個匿名類的實例的隱式引用。將實驗只是爲了驗證,然後編輯。 – 2013-03-02 17:50:28

+0

@PaulDraper:對。原因是正確的,但刪除了一個級別......這就解釋了爲什麼解決方案也是如此:)請參閱我編輯的答案以獲得完整的解釋 - 顯然,如果您仍然有任何問題,請告訴我。 – 2013-03-02 17:56:25