This is an old question, but I thought I would add my 2c.
Even if you recurse through the whole object, you cannot guarantee that your object implements serializable. If you have a member variable that is abstract or is an interface, you cannot say if the object you eventually store will or will not be serialized. If you know what you are doing, it would serialized.
This answer provides a solution by populating your objects with random data, but this is probably overkill.
I have created a class that does recurse through the structure ignoring interfaces, java.lang.Object and abstract classes.
package au.com.tt.util.test;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public final class SerializableChecker
{
public static class SerializationFailure
{
private final String mContainingClass;
private final String mMemberName;
public SerializationFailure(String inNonSerializableClass, String inMemberName)
{
mContainingClass = inNonSerializableClass;
mMemberName = inMemberName;
}
public String getContainingClass()
{
return mContainingClass;
}
public String getMemberName()
{
return mMemberName;
}
public String getBadMemberString()
{
if (mMemberName == null)
return mContainingClass;
return mContainingClass + "." + mMemberName;
}
@Override
public String toString()
{
return "SerializationFailure [mNonSerializableClass=" + mContainingClass + ", mMemberName=" + mMemberName + "]";
}
}
private static class SerializationCheckerData
{
private Set<Class<?>> mSerializableClasses;
SerializationCheckerData()
{
mSerializableClasses = new HashSet<Class<?>>();
}
boolean isAlreadyChecked(Class<?> inClass)
{
return mSerializableClasses.contains(inClass);
}
void addSerializableClass(Class<?> inClass)
{
mSerializableClasses.add(inClass);
}
}
private SerializableChecker()
{ }
public static SerializationFailure isFullySerializable(Class<?> inClass)
{
if (!isSerializable(inClass))
return new SerializationFailure(inClass.getName(), null);
return isFullySerializable(inClass, new SerializationCheckerData());
}
private static SerializationFailure isFullySerializable(Class<?> inClass, SerializationCheckerData inSerializationCheckerData)
{
for (Field field : declaredFields(inClass))
{
Class<?> fieldDeclaringClass = field.getType();
if (field.getType() == Object.class)
continue;
if (Modifier.isStatic(field.getModifiers()))
continue;
if (field.isSynthetic())
continue;
if (fieldDeclaringClass.isInterface() || fieldDeclaringClass.isPrimitive())
continue;
if (Modifier.isAbstract(field.getType().getModifiers()))
continue;
if (inSerializationCheckerData.isAlreadyChecked(fieldDeclaringClass))
continue;
if (isSerializable(fieldDeclaringClass))
{
inSerializationCheckerData.addSerializableClass(inClass);
SerializationFailure failure = isFullySerializable(field.getType(), inSerializationCheckerData);
if (failure != null)
return failure;
else
continue;
}
if (Modifier.isTransient(field.getModifiers()))
continue;
return new SerializationFailure(field.getDeclaringClass().getName(), field.getName());
}
return null;
}
private static boolean isSerializable(Class<?> inClass)
{
Set<Class<?>> interfaces = getInterfaces(inClass);
if (interfaces == null)
return false;
boolean isSerializable = interfaces.contains(Serializable.class);
if (isSerializable)
return true;
for (Class<?> classInterface : interfaces)
{
if (isSerializable(classInterface))
return true;
}
if (inClass.getSuperclass() != null && isSerializable(inClass.getSuperclass()))
return true;
return false;
}
private static Set<Class<?>> getInterfaces(Class<?> inFieldDeclaringClass)
{
return new HashSet<Class<?>>(Arrays.asList(inFieldDeclaringClass.getInterfaces()));
}
private static List<Field> declaredFields(Class<?> inClass)
{
List<Field> fields = new ArrayList<Field>(Arrays.asList(inClass.getDeclaredFields()));
Class<?> parentClasses = inClass.getSuperclass();
if (parentClasses == null)
return fields;
fields.addAll(declaredFields(parentClasses));
return fields;
}
}