views:

1754

answers:

7

I have the following interface:

public interface Result<T extends Serializable> extends Serializable{
    T getResult();
}

With that interface, I can not define a variable of type

Result<List<Integer>>

because List is not serializable.

However, if I change the interface to this:

public interface Result<T> extends Serializable{
    T getResult();
}

It now becomes impossible to implement, with compile time checking, because there is no guarantee that T is serializable, and the whole point of the class is to store the result so that I can get it back later, possibly after a transfer over the internet.

My question is this, is there a way to declare a variable so that it is of two types, or is there some other way to do this that I am not seeing? So, maybe something like:

(List<Integer> extends Serializable) value = new ArrayList();

I am trying to put as much of the burden of this problem on the implementation, so that future consumers of this interface are unaware of the problem.

Thanks for your help!

Here is a more detailed example of what I am trying to do: I am calling a server, The result of which I want to store in a result object, but I do not want to have to deal with casting. So, each method that can be called on the server will define the type of the result by using generics. This result object will also store other meta-data about the result, for example maybe there was too much information to return. In many cases I want to use a list, but the List interface is not serializable, although many of the implementations are.

So, how to I specify that the type used must be Serializable, but still allow for a List(and not a specific implementation of List) to be used?

A: 

Initial thought. If you are planning on using serialization for any kind of long term data storage, don't. Serialization is not guaranteed to work between invocations of the JVM.

You probably should not be extending Serializable unless you are adding functionality related to Object serialization. Instead, your class that implements Result should also implement Serializable.

Matthew Brubaker
I am not using it for long term data storage. Sorry, when I said "the whole point of the class is to store the result", I meant for later in the same instance of the program.
A: 

If your intention is to use the Result type for lists in general, and what you want is to make sure the elements of the list are serializable, you could define it like this:

public interface Result<T extends List<? extends Serializable>> {}

That way, you could define something like:

Result<List<Integer>> r;

But something like this would not compile:

Result<List<List>> r;

Now if you want to use the result both for, say, Integer, and for List, then the type is not required to be serializable, right? In that case I don't really understand what your goal is.

Fabian Steeg
It needs to account for any object that is serializable, not just List. It needs to be serializable so that it can be transferred between the server and the client.
If the type needs to be serializable, then it can't be List. If you want to use List, you can't demand it to be serializable. I think I don't see what kind of solution you are looking for, sorry.
Fabian Steeg
It can be a List and be Serializable. For example, java.util.ArrayList.
Yes, but any List is not. You seem to want two opposing things at the same time (allowing only serializable types to be used and allowing java.util.List to be used).
Fabian Steeg
I don't understand how those two things are opposing. I want to let my consumers use the List implementation of their choice, as long as it is Serializable.
From your comment to mmyers' answer I understood that declaring as "Result<ArrayList<Integer>>" is not what you want (which to me seems to fit "let my consumers use the List implementation of their choice, as long as it is Serializable"). I probably don't grasp your exact use case.
Fabian Steeg
There are really three ways to consume it. Get the result, set the result and transfer the result. To transfer, you must know it is Serializable. To get, you must know it is a List. To set, you must know it is a List and Serializable. I want to have each know only what they need to. Hope that helps!
A: 

You could simply declare the variable as Result<ArrayList<Integer>>. As long as you still program to the List interface, you haven't really sacrificed replaceability.

I was going to also suggest creating a new interface ListResult:

public interface ListResult<T extends Serializable & List<E extends Serializable>>
        implements Result<T> {
    T getResult();
}

but then you would still have to declare the variable as ListResult<ArrayList<Integer>>. So I'd go the simpler route.

Michael Myers
I don't want to force the server to specify the implementation that will be returned because then it will not be able to be passively changed.
A: 

You kind of can, I think:

public class Thing<T extends Serializable> implements Serializable {
    private static class Holder<V extends Serializable> {
        private final V value;
        private Holder(V value) {
            this.value = value;
        }
    }
    private Holder<? extends List<T>> holder;
    private <V extends List<T> & Serializable> void set(V value) {
        holder = new Holder<V>(value);
    }
}

Does that look ugly enought for you?

Can I suggest that you do not attempt to enforce implementing Serializable using the Java static type system? It's only an interface because annotations weren't about back then. It's just not practical to enforce it. OTOH, you could enforce it using a type checker that does static analysis in a closed system.

Tom Hawtin - tackline
A: 

The way I ended up solving this problem is to use this as the interface:

public interface Result<T> extends Serializable{
    T getResult();
}

Then create an implementation for each of the different types of collections as well as one for any object.

So, for example here is what the ListResult class would look like:

public class ListResult<T> implements Result<List<T>>{
    public List<T> getResult(){
        //return result
    }

    public <V extends List<T> & Serializable> void setResult(V result){
        //store result
    }
}
+5  A: 

You need to declare your variable type as Result<? extends List<Integer>>.

The type checking knows that List isn't serializable, but a subtype of List can be serializable.

Here is some sample code. The interface implementation was just done with anonymous inner classes. You can see that the getResult will return a List<Integer> on the 2nd object

   Result<Integer> res = new Result<Integer>() {

  Integer myInteger;

  private static final long serialVersionUID = 1L;

  @Override
  public Integer getResult() {
   return myInteger;
  }

  @Override
  public void addResult(Integer input) {
   this.myInteger = input;
  }
 };

 Integer check = res.getResult();


 Result<? extends List<Integer>> res2 = new Result<ArrayList<Integer>>() {

  ArrayList<Integer> myList;

  private static final long serialVersionUID = 1L;

  @Override
  public ArrayList<Integer> getResult() {
   return myList;
  }

  @Override 
  public void addResult(ArrayList<Integer> input) {
   this.myList = input;
  }

 };

 List<Integer> check2 = res2.getResult();

Edit: Made the example more complete by implementing a void addResult(T input) interface method

Patrick
Awesome! I was beginning to think there was not a way to do it. Thanks!
A: 

Although the List interface doesn't implement Serializable, all of the built-in Collection implementations do. This is discussed in the Collections Implementations tutorial.

The Collections Design FAQ has a question "Why doesn't Collection extend Cloneable and Serializable?" which talks about why Sun designed it without extending Serializable.

R. Bemrose
Right, I understand the reasoning behind it all, but in this case I needed to make sure that the List that was being used was serializable.