I am trying to implement a code analyser which takes as input another Java file. For each Variable Declaration, I want to check if the type that the variable belongs to is generic or not. Is there a simple way to do that?
For example, I would like for it to:
isGenericType("HashSet") -> true
isGenericType("int") -> false
I can possibly create a registry containing all generic types, but the problem with that would be if I implement a custom generic type, then I would have to update the registry each time. Is there some simple solution to this?
To examine a generic type and its type parametersGet an instance of Type that represents the generic type. In the following code, the type is obtained using the C# typeof operator ( GetType in Visual Basic, typeid in Visual C++). See the Type class topic for other ways to get a Type object.
Generic is a class which allows the user to define classes and methods with the placeholder. Generics were added to version 2.0 of the C# language. The basic idea behind using Generic is to allow type (Integer, String, … etc and user-defined types) to be a parameter to methods, classes, and interfaces.
Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.
Even if HashSet
can be generic, the type HashSet
itself (without <T>
) is a raw type. Therefore, I would go with the approach of scanning the actual type of the declared variable, maybe applying a regex on the type to see if the angle brackets exist:
isGenericType --> matches the pattern [a-zA-Z_\$][\w\$]*<[a-zA-Z_\$][\w\$]*>
If you want to strictly account for a valid identifer, you can check its definition in the Java Language Specification:
Identifier:
IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
IdentifierChars:
JavaLetter {JavaLetterOrDigit}
JavaLetter:
any Unicode character that is a "Java letter"
JavaLetterOrDigit:
any Unicode character that is a "Java letter-or-digit"
A "Java letter" is a character for which the method
Character.isJavaIdentifierStart(int)
returns true.A "Java letter-or-digit" is a character for which the method
Character.isJavaIdentifierPart(int)
returns true.
This will test by object, class or class name.
Update: Several revisions of this code based on helpful comments have provided some interesting test cases. However, ultimately an appropriate solution to the problem depends on precisely how the question is defined. Refer to earlier revisions and comments.
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
public class GenericTest
{
public static void main(String[] args)
{
try
{
new GenericTest().testAll();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
public void testAll() throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
Object a = new HashMap<String, Object>();
Object b = new HashMap();
int c = 0;
isGeneric(a);
System.out.println("\n");
isGeneric(b);
System.out.println("\n");
isGeneric(c);
System.out.println("\n");
isGeneric("java.util.HashMap");
System.out.println("\n");
isGeneric("java.lang.Integer");
System.out.println("\n");
isGeneric(new TestA());
System.out.println("\n");
isGeneric(new TestB());
System.out.println("\n");
isGeneric(new TestB<String>());
System.out.println("\n");
isGeneric(new TestC());
System.out.println("\n");
isGeneric(new TestD());
System.out.println("\n");
isGeneric(new TestE());
System.out.println("\n");
return;
}
public static void isGeneric(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
GenericTest.isGeneric(Class.forName(className));
return;
}
public static boolean isGeneric(Object o)
{
return isGeneric(o.getClass());
}
public static boolean isGeneric(Class<?> c)
{
boolean hasTypeParameters = hasTypeParameters(c);
boolean hasGenericSuperclass = hasGenericSuperclass(c);
// boolean hasGenericSuperinterface = hasGenericSuperinterface(c);
// boolean isGeneric = hasTypeParameters || hasGenericSuperclass || hasGenericSuperinterface;
boolean isGeneric = hasTypeParameters || hasGenericSuperclass;
System.out.println(c.getName() + " isGeneric: " + isGeneric);
return isGeneric;
}
public static boolean hasTypeParameters(Class<?> c)
{
boolean flag = c.getTypeParameters().length > 0;
System.out.println(c.getName() + " hasTypeParameters: " + c.getTypeParameters().length);
return flag;
}
public static boolean hasGenericSuperclass(Class<?> c)
{
Class<?> testClass = c;
while (testClass != null)
{
Type t = testClass.getGenericSuperclass();
if (t instanceof ParameterizedType)
{
System.out.println(c.getName() + " hasGenericSuperclass: " + t.getClass().getName());
return true;
}
testClass = testClass.getSuperclass();
}
return false;
}
public static boolean hasGenericSuperinterface(Class<?> c)
{
for (Type t : c.getGenericInterfaces())
{
if (t instanceof ParameterizedType)
{
System.out.println(c.getName() + " hasGenericSuperinterface: " + t.getClass().getName());
return true;
}
}
return false;
}
public interface TestX<X> { }
public interface TestY extends TestX<String> { }
public class TestA implements TestY { }
public class TestB<V> extends TestA { }
public class TestC extends TestB<String> { }
public class TestD extends TestA { }
public class TestE extends TestC { }
}
The results of running the above code:
java.util.HashMap hasTypeParameters: 2
java.util.HashMap hasGenericSuperclass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
java.util.HashMap isGeneric: true
java.util.HashMap hasTypeParameters: 2
java.util.HashMap hasGenericSuperclass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
java.util.HashMap isGeneric: true
java.lang.Integer hasTypeParameters: 0
java.lang.Integer isGeneric: false
java.util.HashMap hasTypeParameters: 2
java.util.HashMap hasGenericSuperclass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
java.util.HashMap isGeneric: true
java.lang.Integer hasTypeParameters: 0
java.lang.Integer isGeneric: false
GenericTest$TestA hasTypeParameters: 0
GenericTest$TestA isGeneric: false
GenericTest$TestB hasTypeParameters: 1
GenericTest$TestB isGeneric: true
GenericTest$TestB hasTypeParameters: 1
GenericTest$TestB isGeneric: true
GenericTest$TestC hasTypeParameters: 0
GenericTest$TestC hasGenericSuperclass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
GenericTest$TestC isGeneric: true
GenericTest$TestD hasTypeParameters: 0
GenericTest$TestD isGeneric: false
GenericTest$TestE hasTypeParameters: 0
GenericTest$TestE hasGenericSuperclass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
GenericTest$TestE isGeneric: true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With