简体   繁体   中英

Java, declare variable with multiple interfaces?

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.

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

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

   T myVar;

}

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.

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.

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 (eg 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 (eg a serializable map). You have to have the interface and some appropriate implementation.

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 {

}

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.

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");
}

不,你几乎需要施展。

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”?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM