Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if type is generic

Tags:

java

generics

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?

like image 846
akhiljain Avatar asked Jun 16 '15 20:06

akhiljain


People also ask

How do you know if a type is generic?

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.

What is generic types in C#?

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.

Is generic type parameter?

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.


2 Answers

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.

like image 166
M A Avatar answered Sep 25 '22 22:09

M A


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
like image 35
vallismortis Avatar answered Sep 26 '22 22:09

vallismortis