开发者

Can I have a type safe map that either contains A or List<A>?

开发者 https://www.devze.com 2023-03-20 06:09 出处:网络
I need a Map container that contains 开发者_StackOverflow中文版either an Item Object or List<Item> Object

I need a Map container that contains 开发者_StackOverflow中文版either an

Item Object

or

List<Item> Object

as its value, and I can get it out without casting, is it possible?


Short answer: No.

Union types don't exist in Java. The closest thing you could do in order to get compile-time type checking would be to create list of some custom wrapper class which contained either an A or a List<A>, something like the following:

public class UnionListWrapper<A> {
    private final A item;
    private final List<A> list;

    public UnionListWrapper(A value) {
        item = value;
        list = null;
    }

    public UnionListWrapper(List<A> value) {
        item = null;
        list = value;
    }

    public Object getValue() {
        if (item != null) return item;
        else return list;
    }
}

At least you wouldn't be able to create instances of this class that weren't either an A or a List<A>, but getting the value back out would still have to just be Object, with associated casting. This could get quite clumsy, and on the whole it's probably not worth it.

In practice I'd probably just have a List<Object> with some comments around being very careful about what data types are accepted. The problem is that even run-time checks (sub-optimal) aren't going to be possible, since Java's generic erasure means that you can't do an instanceof A check at runtime (doubly so on the generic parameter of the List).


No, you can't do this in a type-safe manner.

From a design standpoint, I'd suggest using only List<Item> as values. If some of them are single-element lists, that's just fine; your code is likely to be much cleaner if you handle all values in the same way.

In fact, I'm curious how you plan to use a map with elements and lists of elements. Do you know which type to expect for a given key a priori?


Sounds like you want a MultiMap -- There is an Apache implementation and a Google implementation


Possibly the type safe heterogeneous container pattern is what you are looking for

http://books.google.com/books?id=ka2VUBqHiWkC&pg=PA142&lpg=PA142&dq=type+safe+heterogeneous+container+pattern&source=bl&ots=yYDfNltZS3&sig=giH1d_rIQ-MumOnrMmiT4OgtM7E&hl=en&ei=880dTqHcJYSyhAeVhqS3Bw&sa=X&oi=book_result&ct=result&resnum=5&ved=0CDUQ6AEwBA#v=onepage&q=type%20safe%20heterogeneous%20container%20pattern&f=false

Addressing the good point raised in the comment below re type erasure - using super type tokens. Taken from http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html, which highlights an issue with the approach.

import java.lang.reflect.*;

public abstract class TypeRef<T> {
    private final Type type;
    protected TypeRef() {
        ParameterizedType superclass = (ParameterizedType)
            getClass().getGenericSuperclass();
        type = superclass.getActualTypeArguments()[0];
    }
    @Override public boolean equals (Object o) {
        return o instanceof TypeRef &&
            ((TypeRef)o).type.equals(type);
    }
    @Override public int hashCode() {
        return type.hashCode();
    }
}

public class Favorites2 {
    private Map<TypeRef<?>, Object> favorites =
        new HashMap< TypeRef<?> , Object>();
    public <T> void setFavorite(TypeRef<T> type, T thing) {
        favorites.put(type, thing);
    }
    @SuppressWarning("unchecked")
    public <T> T getFavorite(TypeRef<T> type) {
        return (T) favorites.get(type);
    }
    public static void main(String[] args) {
        Favorites2 f = new Favorites2();
        List<String> stooges = Arrays.asList(
            "Larry", "Moe", "Curly");
        f.setFavorite(new TypeRef<List<String>>(){}, stooges);
        List<String> ls = f.getFavorite(
            new TypeRef<List<String>>(){});
    }
} 


Not without writing a wrapper class (WrapsA) which holds either a single A or a List, and making your list be List


Functional Java provides a disjoint union data type called Either<A, B>, so you can define a Map<Key, Either<Item, List<Item>>>.

0

精彩评论

暂无评论...
验证码 换一张
取 消