Adam Varhegyi
Adam Varhegyi

Reputation: 9924

How to serialize a Class with an Interface instance attribute? Got NotSerializableException

I have a class:

public class MyEntity implements Serializable {

    public MyInterface myInterface;

    public String name;
    public int    value;

    public MyEntity(String name, int value) {
        this.name = name;
        this.value = value;
    }
}

And an Interface:

public interface MyInterface {
    void customFunction(int value);
}

The point of this structure would be to fulfill the purpose of each and every instance of MyClass could have different implementation of customFunction(value).

So just like:

MyEntity myEntity = new MyEntity("My entity", 100);
    myEntity.myInterface = new MyInterface() {
        @Override
        public void customFunction(int value) {
            //This is my custom function.
            //This can be different for every instance of MyEntity class
        }
    };

However If I would like to serialize an instance of MyClass, I get NotSerializableException.

java.io.NotSerializableException: com.adamvarhegyi.duelsofcodrer.controller.MainActivity$1 at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1344) at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1651) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1497) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1461) at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:959) at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:360) at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1054) at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1384) at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1651) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1497) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1461) at com.adamvarhegyi.duelsofcodrer.controller.MainActivity.onCreate(MainActivity.java:62) at android.app.Activity.performCreate(Activity.java:6251) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) at android.app.ActivityThread.-wrap11(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

So my question is how could I achieve this? How could I serialize class instances with Interface instance attributes?

Update:

This is the full code to make it clearer:

(I have made inner classes from MyEntity and MyInterface to make it more simplified.)

package com.adamvarhegyi.duelsofcodrer.controller;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;

import com.adamvarhegyi.duelsofcodrer.R;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class MainActivity extends Activity {

public interface MyInterface extends Serializable {
    void customFunction(int value);
}

public class MyEntity implements Serializable {

    public MyInterface myInterface;

    public String name;
    public int    value;

    public MyEntity(String name, int value) {
        this.name = name;
        this.value = value;
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    MyEntity myEntity = new MyEntity("My entity", 100);

    myEntity.myInterface = new MyInterface() {
        @Override
        public void customFunction(int value) {
            //This is my custom function.
            //This can be different for every instance of MyEntity class
        }
    };


    //Trying to serialize
    try {
        FileOutputStream fos = openFileOutput("myfile", Context.MODE_PRIVATE);
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(myEntity);
        oos.close();
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}
}

Upvotes: 4

Views: 1509

Answers (2)

rafaelc
rafaelc

Reputation: 59284

When you define your class as a public class MyEntity implements Serializable you are basically defining that all atributes of MyEntity should be serializable. However, MyInterface is not, thus, the attribute public MyInterface myInterface cannot be serialized, and you get the NotSerializableException .

To fix that, your interface should be an instace of Serializable, so it should extend it.

public interface MyInterface extends Serializable 
{
    void customFunction(int value);
}

That way, MyEntity will have all attributes serializable, and no errors will be thrown. It is important to notice that all basic data types such as int, float etc can be serialized, as well as Strings.

EDIT

After much thinking and reading and re-reading the code, I've found your problem. When you do the following :

myEntity.myInterface = new MyInterface() {
    @Override
    public void customFunction(int value) {
        //This is my custom function.
        //This can be different for every instance of MyEntity class
    }
};

you are actually creating a brand-new nameless inner class which implements MyInterface. However, as this is an inner class, it makes reference to its parent class which is Activity. Activity is not serializable, so that is why it throws NotSerializableException.

How to fix that?

It will not be possible for you to create custom classes to-go which are Serializable. However, there is one way out.

You can either create a new Class (e.g. named CustomInterfaceImplementation ) in a separate file, and make instances of it, or make an inner class of your Activity which is static (note that this Activity will not be created inside your onCreate() method, because that wouldn't make any sense. It should be created outside any method's scope and inside the Activity scope).

So, for instance, you could have something like:

CustomInterfaceImplementation customImpl = new CustomInterfaceImplementation();
myEntity.myInterface = customImpl;

The above code would work, being CustomInterfaceImplementation either a static class within your MainActivity or a separate public java class.

But what if I want to make on-the-go classes that varies from obj to obj?

As I said, it will be dificult to make completely new classes that are Serializable. HOWEVER, you could extract all common code between all possible implementations, and just pass variables that could, well, vary. That way, you would maintain the Serializable characteristic, and may be able to achieve your goal.

Upvotes: 6

DMO-NZ
DMO-NZ

Reputation: 71

Either make MyInterface Serializable, or add transient to myInterface like so:

    public class MyEntity implements Serializable {
        public transient MyInterface myInterface;
        // ...
    }

Making myInterface transient marks the variable to not be serialized.

Upvotes: 0

Related Questions