简体   繁体   English

涉及 generics 的类型安全异构容器

[英]Typesafe heterogeneous containers involving generics

I need to create a container that provides a way for me to store elements of generic type, kind of like this effective java pattern but storing generic things我需要创建一个容器,为我提供一种存储通用类型元素的方法,有点像这种有效的 java 模式,但存储通用的东西

Is it possible to create a typesafe heterogeneous container where generic typed things are involved?是否可以创建一个涉及泛型类型事物的类型安全的异构容器?

<T> void put(T ele, SomeTypedThing<T> genEle);
<T> SomeTypedThing<T> get(T ele);

I am fine to add the Class<T> as method param.我可以将Class<T>添加为方法参数。 example:例子:

public static class Container {

    Map<Class<?>, Set<?>> store = new HashMap<>();

    public <T> void put(Set<T> ele, Class<T> type) {
        store.put(type, ele);
    }

    public <T> Set<T> get(Class<T> type) {
        return store.get(type);
    }
}

would it be possible to achieve this?有可能做到这一点吗?

Set<?> raw = store.get(type);
Set<T> typed = // some magic;

how, or why not?如何,或者为什么不? is it something that java doesn't do or is it something fundamental (so no language can do, or just doesn't make sense to do)是 java 没有做的事情还是基本的事情(所以没有语言可以做,或者只是没有意义做)

The problem is with the wildcard parameter on the Set .问题在于Set上的通配符参数。 Instead of using a Set<?> , make it a Set<Object> , and everything works:与其使用Set<?> ,不如将其Set<Object> ,一切正常:

public static class Container {

    Map<Class<?>, Set<Object>> store = new HashMap<>();

    public <T> void put(T ele, Class<T> type) {
        store.putIfAbsent(type, new HashSet<>());
        store.get(type).add(ele);
    }

}

The difference between Set<?> and Set<Object> is this: Set<?>Set<Object>的区别在于:

A Set<?> could be a Set of any type - it could be a Set<String> or a Set<Integer> . Set<?>可以是任何类型的Set - 它可以是Set<String>Set<Integer> And the java compiler wants to make sure that you are not trying to add a String object to a Set<Integer> . java 编译器希望确保您没有尝试将String object 添加到Set<Integer>

On the other hand, a Set<Object> is just a Set that can contain instances of the Object class.另一方面, Set Set<Object>只是一个可以包含Object class 实例的 Set。 And since String and Integer are both subclasses of Object , you can easily store strings and Integers into such a set.由于StringInteger都是Object的子类,因此您可以轻松地将字符串和整数存储到这样的集合中。

Adding the method添加方法

    public <T> Set<T> get(Class<T> type) {
        return (Set<T>) store.get(type);
    }

to the class gives a compiler warning about an unchecked cast.到 class 会给出关于未经检查的强制转换的编译器警告。 This warning can be safely ignored here, because you know that you added only elements of type T to that Set .此处可以安全地忽略此警告,因为您知道您只将T类型的元素添加到该Set中。

Thomas Klägers solution is an answer that also came to my head. Thomas Klägers 的解决方案也是我想到的答案。 Creating an fully functioning heterogeneous container which works like this:创建一个功能齐全的异构容器,其工作方式如下:

Set<?> raw = store.get(type);
Set<T> typed = // some magic;

But as far as I know, It is not possible to use container You mentioned with that 'smooth' code I quoted above.但据我所知,不可能使用你提到的容器和我上面引用的“平滑”代码。 However it is usable and You can get sets of Your stored by class sets.但是它是可用的,您可以通过 class 集获取您存储的集。 Here goes the code:代码如下:

public class Container {

    Map<Class<?>, Set<Object>> container = new HashMap<>();

    public <T> void put(T e, Class<?> type) {
        container.putIfAbsent(type, new HashSet<>());
        container.get(type).add(e);
    }

    public <T> Set<T> get(Class<T> type) {
        @SuppressWarnings("unchecked") //It is fine to ignore warnings here
        Set<T> res = (Set<T>) container.get(type);
        return res;
    }
}

And working example of storing ang retreiving Containers elements: public class Run {以及存储 ang retreiving Containers 元素的工作示例: public class Run {

public static void main(String[] args) {
    Foo foo = new Foo();
    Bar bar = new Bar();

    Container con = new Container();
    con.put(foo, foo.getClass());
    con.put(bar, bar.getClass());

    Set<? extends Foo> foos = con.get(foo.getClass());
    Set<? extends Bar> bars = con.get(bar.getClass());

    //here You can use Your sets as ususal
}

If approach <? extends Foo>如果方法<? extends Foo> <? extends Foo> and usage is fine for You, it's working solution. <? extends Foo>并且使用对您来说很好,它是有效的解决方案。 Above that if You work in Java 10+, there's possibility for that 'dirty' declaration omission.除此之外,如果您在 Java 10+ 中工作,则可能会遗漏“脏”声明。 Just declare it as var and poof, its hidden.只需将其声明为var和 poof,它是隐藏的。

I stumbled upon this problem a few weeks ago as I needed a typesafe heterogenous container (THC) for literally any object (including generic interface implementations) AND any number of them (like two keys that provide a String for example).几周前我偶然发现了这个问题,因为我需要一个类型安全的异构容器 (THC),用于几乎任何 object(包括通用接口实现)和任意数量的它们(例如提供字符串的两个键)。

The previous two answers are "weak" THCs because they do not provide compile time type safety.前两个答案是“弱” THC,因为它们不提供编译时类型安全。

Although this question is rather old, I'd like to provide another approach.尽管这个问题相当古老,但我想提供另一种方法。

The "why" it is not possible with a generic type is based on Java's type erasure .泛型类型不可能的“为什么”是基于 Java 的类型擦除 THCs are all about parameterizing the keys. THC 都是关于参数化密钥的。 So you can use a key object that wraps the class type instead of using the class type as key.因此,您可以使用包装 class 类型的密钥 object 而不是使用 class 类型作为密钥。

For example: Key-Class:例如: 键类:

static class Type<T> {
    private final Class<T> object_type;

    public Type(String name, Class<T> object_type){
        this.object_type = object_type;
    }
    public Class<T> getObjectType() {
        return object_type;
    }
}

Container-Class:容器类:

static class Container{

    private final Map<Type<?>, Object> properties = new HashMap<>();

    public <T> T get(Type<T> type){
        return type.getObjectType().cast(properties.get(type)); //no compiler complaints
    }

    public <T> void put(Type<T> type, T value){
        properties.put(type, type.getObjectType().cast(value)); //no compielr complaints
    }
}

Since we can't provide Set<Foo>.class as object type we have like two possibilites.由于我们不能提供Set<Foo>.class作为 object 类型,我们有两种可能。 Either we use inheritence or we use composition.我们要么使用继承,要么使用组合。

Inheritence:继承:

static class IntHashSet extends HashSet<Integer>{}

Composition:作品:

static class IntSetComposition{

    private final Set<Integer> set;
    public IntSetComposition(Set<Integer> set){
        this.set=set;
    }
    public Set<Integer> getSet(){
        return this.set;
    }
}

How to use all this:如何使用这一切:

public static void main(String[] args) {
    Type<String> string_type = new Type<>("string_type_1", String.class);

    Type<Integer> int_type = new Type<>("int_type_1", Integer.class);

    Type<IntHashSet> int_set = new Type<>("int_hashset", IntHashSet.class);        
    Type<IntSetComposition> int_set_comp = new Type<>("int_set_comp", IntSetComposition.class);        

    Container container = new Container();

    String s = container.get(string_type); //no compiler complaints
    int i = container.get(int_type); //no compiler complaints

    IntHashSet set = container.get(int_set); //no compiler complaints
    Set<Integer> set2 = container.get(int_set_comp).getSet(); //no compiler complaints

    String s2 = container.get(int_type); //the compiler does not like this!
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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