SerializableなクラスのserialVersionUID定義を不要にする方法


## 使い方

こんな感じ。

```java:
public class HogeTest {
    @Test
    public void test() throws Exception {
        Hoge before = new HogeImpl();
        Hoge after = Hoge.deserialize(HogeImpl.class, before.serialize());
        assertEquals(before, after);
    }
}
```

## やり方

こんな感じのクラスをベースにすればOKです♪

```java:
abstract public class Hoge implements Externalizable {

    private static Map<String, Field[]> classFields = new ConcurrentHashMap<>();

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        for (Field field : getSortedFields()) {
            try {
                Object value = field.get(this);
                out.writeObject(value);
            } catch (Exception e) {
                continue;
            }
        }
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
        for (Field field : getSortedFields()) {
            try {
                Object value = in.readObject();
                field.set(this, value);
            } catch (Exception e) {
                continue;
            }
        }
    }


    private synchronized Field[] getSortedFields() {
        Class<?> clazz = this.getClass();
        Field[] fields = classFields.get(clazz.getName());
        if (null == fields) {
            fields =
                getSortedFields(clazz, clazz, new TreeMap<String, Field>());
            classFields.put(clazz.getName(), fields);
        }
        return fields;
    }

    private Field[] getSortedFields(Class<?> parent, Class<?> clazz,
            TreeMap<String, Field> treeMap) {
        for (Field field : clazz.getDeclaredFields()) {
            int mod = field.getModifiers();
            if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod)) {
                String key = field.getName();
                Field value;
                try {
                    value = parent.getField(key);
                } catch (Exception e) {
                    continue;
                }
                treeMap.put(key, value);
            }
        }
        Class<?> superClass = clazz.getSuperclass();
        if (Object.class == superClass) {
            return treeMap.values().toArray(new Field[] {});
        }
        return getSortedFields(parent, superClass, treeMap);
    }

    public byte[] serialize() {
        ObjectOutputStream out = null;
        try {
            ByteArrayOutputStream byteArrayOutputStream =
                new ByteArrayOutputStream();
            out =
                new ObjectOutputStream(new BufferedOutputStream(
                    byteArrayOutputStream));
            out.writeObject(this);
            out.flush();
            byte[] bytes = byteArrayOutputStream.toByteArray();
            return bytes;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (null != out) {
                try {
                    out.close();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    public static <T> T deserialize(Class<T> type, byte[] bytes) {
        ObjectInputStream in = null;
        try {
            in =
                new ObjectInputStream(new BufferedInputStream(
                    new ByteArrayInputStream(bytes)));
            @SuppressWarnings("unchecked")
            T obj = (T) in.readObject();
            return obj;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
```