19

In Java, is it possible to declare a field/variable whose type is multiple interfaces? For example, I need to declare a Map that is also Serializable. I want to make sure the variable references a serializable map. The Map interface does not extend Serializable, but most of Map's implementations are Serializable.

I'm pretty sure the answer is no.

Follow up: I'm fully aware of creating a new interface that extends both Map and Serializable. This will not work as existing implementations (such as HashMap) do not implement my new interface.

Steve Kuo
  • 61,876
  • 75
  • 195
  • 257

9 Answers9

13

You can do it with generics, but it's not pretty:

class MyClass<T,K,V extends Serializable & Map<K,V>> {

   T myVar;

}
skaffman
  • 398,947
  • 96
  • 818
  • 769
7

There is no need to declare the field/variable like that. Especially since it can only be tested runtime and not compile time. Create a setter and report an error should the passed Map not implement Serializable.

The answers recommending that you create your own interface are of course not very practical as they will actively prohibit sending in things that are Maps and Serializable but not your special interface.

Fredrik
  • 5,759
  • 2
  • 26
  • 32
  • Even if the `Map` implements `Serializable` it does not necessarily mean that it is serialisable. But yes Java serialisability is a runtime issue in Java. – Tom Hawtin - tackline Jul 13 '09 at 20:43
  • Unfortunately for Java, this is the right answer and deserves to be at the top. – hythlodayr Jul 13 '09 at 20:44
  • You missed the important part of my answer... You accept the Map and THEN check if the object is an instanceof Serializable. You can of course do it the other way around but then you would get an interface that doesn't really tell the user what kind of collection you want. – Fredrik Jul 13 '09 at 20:45
  • @Tom, my previous comment was to you. – Fredrik Jul 13 '09 at 20:46
7

It's possible to do this using some generics tricks:

    public <T extends Map<?,?> & Serializable> void setMap(T map)

The above code uses generics to force you to pass a map which implements both interfaces. However, note that a consequence of this is that when you actually pass it the maps, they will probably need to be either marked as serializable or of a map type which is already serializable. It also is quite a bit more difficult to read. I would document that the map must be serializable and perform the test for it.

deterb
  • 3,994
  • 1
  • 28
  • 33
5
public interface MyMap extends Map, Serializable {
}

will define a new interface that is the union of Map and Serializable.

You obviously have to then provide a suitable implementation of this (e.g. MyMapImpl) and you can then provide variable references of the type MyMap (or Map, or Serializable, depending on the requirements).

To address your clarification, you can't retrofit behaviour (e.g. a serializable map). You have to have the interface and some appropriate implementation.

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • 2
    I was assuming that he wanted to assign existing objects to the variable, e.g. a HashMap object. – skaffman Jul 13 '09 at 20:29
  • 2
    I think he wants to have a serializable map as a variable signature, but still be able to use HashMap, TreeMap, etc. I.e., MyMap map = new HashMap(); isn't going to work I think. – JeeBee Jul 13 '09 at 20:32
5

I voted up Brian's answer, but wanted to add a little higher-level thought..

If you look through the SDK, you'll find that they rarely (if ever) pass around actual collection objects.

The reason for that is that it's not a very good idea. Collections are extremely unprotected.

Most of the time you want to make a copy before passing it off and pass the copy so that any modifications to the collection won't change the environment for something else that's relying on it. Also, threading becomes a nightmare--even with a synchronized collection!

I've seen two solutions, one is to always extract an array and pass it. This is how the SDK does it.

The other is to ALWAYS wrap collections in a parent class (And I mean encapsulate, not extend). I've gotten into this habit and it's very worth while. It doesn't really cost anything because you don't duplicate all the collection methods anyway (actually you rarely duplicate any of them). In fact what you end up doing is moving "Utility" functionality from other classes distributed all over your code into the wrapper class, which is where it should have been in the first place.

Any method with a signature that matches "method(collection,...)" should almost certainly be a member method of that collection, as should any loops that iterate over the collection.

I just have to throw this out every now and then because it's one of those things I didn't get for a while (because nobody championed the concept). It always seems like it's going to have some drawback but having done this for a while and seeing the problems it solved and code it eliminated, I can't even imagine any possible drawbacks myself, it's just all good.

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • I agree with this. It's not clear to me why accepting arbitrary existing `Map`s is desirable, rather than some semantically meaningful interface specific to the code at hand. – David Moles Jul 14 '09 at 07:33
3

You can achieve this by making your own Interface, which extends the interfaces you want

public interface SerializableMap<K, V> extends Map<K, V>, Serializable {

}
Chris Arguin
  • 11,850
  • 4
  • 34
  • 50
1

In my case it worked just to declare the concrete type:

    HashMap<String, String> mySerializableMap = new HashMap<>();

It allowed me to use the Map methods (like put) and pass the map to methods that required a Serializable, without casting. Not perfect when we’ve learned to program towards interfaces, but good enough for me in the situation I was in.

If you really insist: As has been noted, declaring a combined interface alone does not solve the problem since the concrete classes we already have do not implement our combined interface even when they do implement each of the two interfaces we combine. I use it as a first step on the way, though. For example:

public interface SerializableMap<K, V> extends Map<K, V>, Serializable {
    // No new methods or anything
}

The next step is also declaring a new class:

public class SerilizableHashMap<K, V> extends HashMap<K, V> implements SerializableMap<K, V> {
    private static final long serialVersionUID = 4302237185522279700L;
}

This class is declared to implement the combined interface and thus can be used wherever one of those types is required. It extends a class that already implements each of the interfaces separately, therefore there’s nothing more we need to do. And now we have got what you asked for. Example of use:

public static void main(String[] args) {
    SerializableMap<String, String> myMap = new SerilizableHashMap<>();

    // myMap works as a Map
    myMap.put("colour1", "red");
    myMap.put("colour2", "green");

    // myMap works as a Serializable too
    consumeSerializable(myMap);
}

public static void consumeSerializable(Serializable s) {
    // So something with the Serializable
}

For most purposes I suggest that this is overkill, but now I have at least presented it as an option.

Link: What does it mean to “program to an interface”?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

You can't really do it if you want to keep using the existing Map implementations.

An alternative would be to make a helper class, and add a method like this one:

public static Serializable serializableFromMap(Map<?, ?> map) {
    if (map instanceof Serializable) {
        return (Serializable)map;
    }
    throw new IllegalArgumentException("map wasn't serializable");
}
jqno
  • 15,133
  • 7
  • 57
  • 84
0

Nope, you'll pretty much need to cast.

Jack Leow
  • 21,945
  • 4
  • 50
  • 55