NotSerializableException в анонимном классе

У меня есть интерфейс для фильтрации элементов:

public interface KeyValFilter extends Serializable{
public static final long serialVersionUID = 7069537470113689475L;
public boolean acceptKey(String iKey, Iterable<String> iValues);
public boolean acceptValue(String iKey, String value);
}

и класс, содержащий элемент типа KeyValFilter.

public class KeyValFilterCollector extends KeyValCollectorSkeleton {
/**
 * 
 */
private static final long serialVersionUID = -3364382369044221888L;
KeyValFilter filter;
public KeyValFilterCollector(KeyValFilter filter){
    this.filter=filter;
}

Когда я пытаюсь инициировать KeyValFilterCollector с анонимным классом, реализующим KeyValFilter:

        new KeyValFilterCollector(new KeyValFilter(){
        private static final long serialVersionUID = 7069537470113689475L;
        public boolean acceptKey(String iKey, Iterable<String> iValues){
            for (String value : iValues) {
                if (value.startsWith("1:"))
                        return true;
            }
            return false;
        }
        public boolean acceptValue(String iKey, String value){
            return value.startsWith("0:");
        }
    });

Я получаю исключение: Исключение в потоке "main" java.io.NotSerializableException Как сделать анонимный класс я написал Serializable?

Ответ 1

Джошуа Блох пишет в своей книге Эффективная Java, 2-е издание, пункт 74:

Внутренние классы не должны реализовывать Serializable. Они используют синтезированные поля, созданные компилятором, для хранения ссылок на окружающие экземпляры и для хранения значений локальных переменных из охватывающих областей. Как эти поля соответствуют определению класса не определены, как и имена анонимных и локальных классов. Поэтому стандартная сериализованная форма внутреннего класса не определена. Однако статический класс-член может реализовать Serializable.

Ответ 2

Обычно проблема, возникающая при сериализации анонимного класса, заключается в том, что охватывающий класс не является сериализуемым (и автор не понял, что сериализация анонимного класса включает сериализацию его охватывающего класса).

Анонимный класс - нестатический внутренний класс. Это означает, что у него есть скрытое поле, которое ссылается на экземпляр окружающего класса. Когда вы создаете его с помощью new KeyValFilter(){ ... }, без явного определения его (например, something.new KeyValFilter(){ ... }), то this неявно используется как экземпляр класса-оболочки (как если бы вы делали this.new KeyValFilter(){ ... }). Поэтому, когда вы сериализуете анонимный класс, который требует сериализации всех его полей, один из которых является экземпляром охватывающего класса, который затем должен быть сериализуемым.

Если вам не нужно использовать какие-либо поля или методы окружающего класса, вместо этого вы должны использовать статический внутренний класс. (Однако он не может быть анонимным или определен внутри метода.)

Ответ 3

Вы можете объявить анонимный класс serializable, но класс только тогда действительно сериализуем, если все его поля сериализуемы.

См. пример:

public static void main(String[] args) throws Exception {
    Object myObj = new Serializable() {
        private static final long serialVersionUID = 1L;
        private String str = "something";
        private Object ns = new Object(){};
    };
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(myObj);
    oos.close();
    System.out.println("Success!");
}

Если вы прокомментируете строку

private Object ns = new Object(){};

Код завершен с успехом, в противном случае NotSerializableException.