简体   繁体   English

Java泛型传递参数

[英]Java generics passing parameters

Hope somebody can help me out of this confussion. 希望有人可以帮助我摆脱这种困扰。

I made this method: 我做了这个方法:

public static <T> void myMethod(Map<Class<T>, MyInterface<T>> map) {
}

Used paramter T in order to make sure that the class used as key is the same as the class used as parameter in MyInterface. 使用参数T以确保用作键的类与用作MyInterface中的参数的类相同。

Now I want to pass a map which different classes as keys, of course, and corresponding implementations of MyInterface. 现在我想传递一个不同类作为键的映射,当然还有MyInterface的相应实现。

But it doesn't work, getting syntax errors because of type parameters. 但它不起作用,因类型参数而导致语法错误。 Here is the code, I hope is self explanatory. 这是代码,我希望是自我解释。

    import java.util.HashMap;
    import java.util.Map;

    public class Test {

    public static void main(String[] args) {
        Map<Class<?>, MyInterface<?>> map = new HashMap<Class<?>, MyInterface<?>>();

    //      Map<Class<Object>, MyInterface<Object>> map = new HashMap<Class<Object>, MyInterface<Object>>();

        map.put(Object.class, new MyObjectImpl());

        //if I use Map<Class<Object>, MyInterface<Object>> I get a compiler error here
        //because map<String> is not map<Object> basically
        map.put(String.class, new MyStringImpl());

        //this would be possible using <?>, which is exactly what I don't want
    //      map.put(String.class, new MyIntegerImpl());

        //<?> generates anyways a compiler error
        myMethod(map);
    }

    //use T to make sure the class used as key is the same as the class of the parameter "object" in doSomething  
    public static <T> void myMethod(Map<Class<T>, MyInterface<T>> map) {

    }

    interface MyInterface<T> {
        void doSomething(T object);
    }

    static class MyObjectImpl implements MyInterface<Object> {
        @Override
        public void doSomething(Object object) {
            System.out.println("MyObjectImpl doSomething");
        }
    }

    static class MyStringImpl implements MyInterface<String> {
        @Override
        public void doSomething(String object) {
            System.out.println("MyStringImpl doSomething");
        }
    }

    static class MyIntegerImpl implements MyInterface<Integer> {
        @Override
        public void doSomething(Integer object) {
            System.out.println("MyIntegerImpl doSomething");
        }
    }
}

You can't do that, because there is no constraint defined in Map 's put() method between the key and the value . 你不能这样做,因为在keyvalue之间没有Mapput()方法中定义的约束。 If you want to assure that your map is populated properly (ie create such constraint), hide the map behind some API that will check the correctness, for example: 如果您想确保正确填充地图(即创建此类约束),请隐藏一些将检查正确性的API后面的地图,例如:

public <T> void registerInterface(Class<T> clazz, MyInterface<T> intf) {
    map.put(clazz, intf);
}

Then, just call the registerInterface instead of manually populating the map. 然后,只需调用registerInterface而不是手动填充地图。

As far as I know, you cannot declare a Map like you describe in Java. 据我所知,你不能像在Java中描述的那样声明一个Map。 All you can do is performing type checking and/or add constraints. 您所能做的就是执行类型检查和/或添加约束。

Guava offers something that approaches your problem with ClassToInstanceMap . Guava提供了一些可以通过ClassToInstanceMap解决问题的方法。 So one way to do this would be to use MapConstraints.constrainedMap (like the example below) 所以这样做的一种方法是使用MapConstraints.constrainedMap (如下例所示)

import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;

import com.google.common.collect.MapConstraint;
import com.google.common.collect.MapConstraints;

public class Main {

    interface MyInterface<T> {
        void doSomething(T object);

        Class<T> getType();
    }

    static class MyObjectImpl implements MyInterface<Object> {
        @Override
        public void doSomething(Object object) {
            System.out.println("MyObjectImpl doSomething");
        }

        @Override
        public Class<Object> getType() {
            return Object.class;
        }
    }

    static class MyStringImpl implements MyInterface<String> {
        @Override
        public void doSomething(String object) {
            System.out.println("MyStringImpl doSomething");
        }

        @Override
        public Class<String> getType() {
            return String.class;
        }
    }

    static class MyIntegerImpl implements MyInterface<Integer> {
        @Override
        public void doSomething(Integer object) {
            System.out.println("MyIntegerImpl doSomething");
        }

        @Override
        public Class<Integer> getType() {
            return Integer.class;
        }
    }

    public static void main(String[] args) throws ParseException {

        Map<Class<?>, MyInterface<?>> map = MapConstraints.constrainedMap(new HashMap<Class<?>, Main.MyInterface<?>>(),
                new MapConstraint<Class<?>, MyInterface<?>>() {
                    @Override
                    public void checkKeyValue(Class<?> key, MyInterface<?> value) {
                        if (value == null) {
                            throw new NullPointerException("value cannot be null");
                        }
                        if (value.getType() != key) {
                            throw new IllegalArgumentException("Value is not of the correct type");
                        }
                    }
                });
        map.put(Integer.class, new MyIntegerImpl());
        map.put(String.class, new MyStringImpl());
        map.put(Object.class, new MyObjectImpl());
        map.put(Float.class, new MyIntegerImpl()); //<-- Here you will get an exception
    }
}

I do not think this is possible : 我不认为这是可能的:

Class<T> only ever accepts T.class as value. Class<T>只接受T.class作为值。 Class<Object> will not accept String.class , even though Object is a superclass of String. Class<Object>不接受String.class ,即使Object是String的超类。

For this reason any map with Class<T> as key can have only one element, with T.class as key value, whatever the value of T . 因此,任何以Class<T>为键的映射都只能有一个元素, T.class作为键值,无论T的值如何。

The compiler will only ever accept a map with a definite value of T as parameter. 编译器只会接受一个定义值为T的映射作为参数。 You cannot write Map<Class<?>, MyInterface<?>> because each ? 你不能写Map<Class<?>, MyInterface<?>>因为每个? is assumed to be different : it does not match Map<Class<T>, MyInterface<T>> which requires T to have the same value. 假设是不同的:它与Map<Class<T>, MyInterface<T>>不匹配Map<Class<T>, MyInterface<T>>后者要求T具有相同的值。

That said, myMethod will only ever accept single-entry maps, which does not seem useful. 也就是说,myMethod只接受单入口地图,这似乎没用。

Change your method signature to 将方法签名更改为

public static <T> void myMethod(Map<Class<? extends T>, MyInterface<? extends T>> map) {

}

now your declaration and invocation should work.. 现在你的声明和调用应该工作..

Map<Class<?>, MyInterface<?>> map = new HashMap<Class<?>, MyInterface<?>>();
    map.put(Integer.class, new MyIntegerImpl());
    map.put(String.class, new MyStringImpl());
    map.put(Object.class, new MyObjectImpl());
    myMethod(map);

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

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