Reputation: 10285
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?
Upvotes: 2
Views: 7951
Reputation: 2413
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!
Upvotes: 2
Reputation: 63698
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
Upvotes: 0
Reputation: 718936
The way to deal with this is to explicitly test for null; e.g.
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.)
Upvotes: 0
Reputation:
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.
Upvotes: 0
Reputation: 34563
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.
Upvotes: 2
Reputation: 110054
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, ...);
Upvotes: 3