简体   繁体   中英

How to determine if parameter is correct type in Generics

I'm writing a map implementation with the following required method signature.

I need to ensure value is of the correct type. I thought this check would cause an exception if two types were not equal, but it doesn't. Is there anyway to check for this?

public boolean containsValue(Object value) {

    try {
        V temp = (V) value;
    } catch (Exception e) {
        throw new ClassCastException("Value is not of correct type");
    }
    ...

Because of type erasure you cannot use the standard instanceof operator. The canonical approach is to pass the class itself or an instance of it somewhere.

Then you can use reflection to check the instance type:

private Class<V> clazz; // somehow this gets set

public boolean containsValue(Object value){

    if(clazz.isInstance(value)){
        // safe:
        V temp = (V) value;
    }

You can also check the inheritance tree using Class.isAssignableFrom :

    if(clazz.isAssignableFrom(value.getClass())){
        // safe:
        V temp = (V) value;
    }

Note this guarantees that this is a type-safe cast but does not require the types to be the same, eg V could be Number , but value could be of type Integer .

As far as getting clazz , you can enforce it in your constructor:

public class MyMap<K,V>{

    private final Class<V> clazz;

    public MyMap(Class<V> clazz){
        this.clazz = clazz;
    }

...

And your initializer for the class might look something like:

 MyMap<Integer,String> foo = new MyMap<Integer,String>(String.class);

I'm generally in favor of passing the type in the constructor for this kind of thing and marking the field as final since the semantics and restrictions would then prevent you from changing that field, which is reasonable since the instance generally relies on compile-time guarantees that assume the type parameters are unchanged.

Why do you need the value to be of type V? The Map interface was defined this way not by accident.

You should rather accept the value being an Object and then invoke value.equals(someOtherValueInYourMap).

For example:

public boolean containsValue(Object value) {
    V v1 = ...;
    V v2 = ...;
    V v3 = ...;
    return value.equals(v1) || value.equals(v2) || value.equals(v3);
}

assuming there are three values in your map v1, v2, v3. It doesn't matter what's the type of v1, v2, v3 and the argument value, cause the equals method is defined in Object and you can always call it to compare objects. If value is really of a different type that can't be compared with V instances, then its equals method will throw an ClassCastException and unless you catch it, it will be propagated further. Don't worry about it.

The reason it doesn't throw an exception is because the map can't contain an object of a different type, so false is the correct answer.

As for your question, thanks to type-erasure, no there is no way of seeing if the passed object is the same as the generic type.

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