简体   繁体   中英

Java: Check if a generic is an int

public class Hashing<Key, Elem>

I want to check if Key is an int, so I place this lines in the constructor:

Key key = null;
if (!(key instanceof Integer)) {
    throw new TypeOfKeyStillNotSupportedException();
}

However, I create one this way:

tHash = new Hashing<Integer, Movie>(max);

And the damn TypeOfKeyStillNotSupportedException() pops. Why is this happening and how can I do it properly?

Thanks in advance.

Edit: Already found that the problem is with key being assigned null. The question is now: How to do the check?

null is not an instanceof anything. Due to type erasure, there's no way to check the type of Key directly at runtime. One option is to make the user pass in Class<Key> to the constructor so that you can check that:

public Hashing(Class<Key> keyType, ...) {
  if (keyType != Integer.class) {
    throw new TypeOfKeyStillNotSupportedException();
  }
  ...
}

...

Hashing<Integer, Foo> hashing = new Hashing<Integer, Foo>(Integer.class, ...);

To save yourself from having to repeat the type arguments, you can create a static factory method:

public static <K, E> Hashing<K, E> create(Class<K> keyType, ...) {
  return new Hashing<K, E>(keyType, ...);
}

...

Hashing<Integer, Foo> hashing = Hashing.create(Integer.class, ...);

If your code has "key = null;" right before the test of instanceof, then the Exception will necessarily be thrown.

The reason being that the instancof operator checks the reference of the type of object being pointed to and not how it is declared to be.

You can try with this simple example and remove the comments accordingly to see the difference:

public static void main(String[] args) {
    //Object obj = new Integer(9);
    Object obj = null;

    if (!(obj instanceof Integer))
        System.out.println("Not Integer.");
    else
        System.out.println("Is Integer");
}

Also, you can find out more details here:

http://download.oracle.com/javase/tutorial/java/nutsandbolts/op2.html

Hope it helps:)


Full blown example of Java Generics:

class GenTest<Key extends Integer, Value>{
    Key key;
    Value val;

    GenTest(Key key, Value val){
        this.key = key;
        this.val = val;

        System.out.println("Key: " + key + " Value: " + val);
    }
}

public class GenericRecap {
    public static void main(String[] args) {
        //Object obj = new Integer(9);
        Object obj = null;

        if (!(obj instanceof Integer))
            System.out.println("Not Integer.");
        else
            System.out.println("Is Integer");

        new GenTest<Integer, String>(9, "nine");
    //new GenTest<String, String>("funny", "nine");  // In-Error
    }
}

Also note that by having 'Key extends Integer', an exception will be thrown during Runtime if you pass that does not subclass Integer. Furthermore, if you're using and IDE that checks for it, it'll be flag as 'Type not within bound' of the GenTest Class.

Floats and Integer all inherit from Number. Thus you can 'extend Number' and then check for 'instanceof Integer' or 'instanceof Float' depending on how you want to use it in your code.

Hope it helps:) Cheers!

Java generics are implemented using type erasure : the generic information is discarded by the compiler after being used for type checking, so at runtime your class is effectively just Hashing<Object, Object> . That's why you can't do runtime checks based on a generic type.

You can add an argument of type Class<Key> to the constructor, and the caller will have to pass the correct class object for the type being used as the key. For example, in a Hashing<Integer, String> , only Integer.class will be accepted as the value of that argument, and you can use that for runtime type checking. The requirement to pass a the key's class object as a parameter makes the constructor call look a little awkward, though.

Given the following from your example:

public class Hashing<K, E>

This makes no sense: because K is a Type that can be set to anything when the class is specialized. I changed the name from Key to K to remove confusion that you might have that Key isn't representing a Type . The general standard for Type names is to use single characters most of the time.

K key = null;
if (!(key instanceof Integer)) 
{
    throw new TypeOfKeyStillNotSupportedException();
}

if you change it to

K key = null;
if (!(key instanceof K)) 
{
    throw new TypeOfKeyStillNotSupportedException();
}

that would be a more Generic solution, but that doesn't make sense either because key will always be a type of K

key can't be null because null is not of any Type

Without having more context to what surrounds the instanceof check I don't know what your intention is.

Checking the Type of a Generic with instanceof is a code smell, and probably the wrong approach regardless.

Look here at how the java.util.HashMap implementation handles this problem.

There is only two places where they use instanceof and those are both legacy methods that accept only Object and are not typesafe.

The way to deal with this is to explicitly test for null; eg

Key key = null;
if (key == null) {
    throw new KeyIsNullException();
} else if (!(key instanceof Integer)) {
    throw new TypeOfKeyStillNotSupportedException();
}

// ... or (for the literal minded) ...

if (key != null && !(key instanceof Integer)) {
    throw new TypeOfKeyStillNotSupportedException();
}

For the record, those people who responded that this was something to do with type erasure are off base. You would get exactly the same behaviour if Key was a definite class or interface.

(On the other hand, if the code had said someObj instanceof Key ... that would be a compilation error because of the type erasure issue.)

You need an isInstance method which is the dynamic equivalent of instanceof operator.

For example,

Integer.class.isInstance(1); //return true
Integer.class.isInstance(null); //return false

Edit :
If you want to test against different class type, write a utility method like this:

static <T> boolean isInstance(Class<T> type, Object obj) {
    return type.isInstance(obj);
}

For example,

Hashing.<String>isInstance(String.class, "hi"); // return true

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