检查类型是否通用

时间:2015-06-16 20:31:09

标签: java generics

我正在尝试实现一个代码分析器,它将另一个Java文件作为输入。对于每个变量声明,我想检查变量所属的类型是否是通用的。有没有一种简单的方法可以做到这一点?

例如,我想它:

isGenericType("HashSet") -> true
isGenericType("int") -> false

我可以创建一个包含所有泛型类型的注册表,但问题是如果我实现自定义泛型类型,那么我每次都必须更新注册表。对此有一些简单的解决方案吗?

4 个答案:

答案 0 :(得分:6)

即使HashSet可以是通用的,类型HashSet本身(没有<T>)也是原始类型。因此,我会采用扫描声明变量的实际类型的方法,可能在类型上应用正则表达式以查看尖括号是否存在:

isGenericType --> matches the pattern [a-zA-Z_\$][\w\$]*<[a-zA-Z_\$][\w\$]*>

如果您想严格考虑有效的身份证明,可以在Java Language Specification中查看其定义:

  

标识符:

     
    

IdentifierChars但不是关键字或BooleanLiteral或NullLiteral

  
     

IdentifierChars:

     
    

JavaLetter {JavaLetterOrDigit}

  
     

JavaLetter:

     
    

任何Unicode字符,即&#34; Java字母&#34;

  
     

JavaLetterOrDigit:

     
    

任何Unicode字符,即&#34; Java字母或数字&#34;

  
     

A&#34; Java letter&#34;是方法Character.isJavaIdentifierStart(int)返回true的字符。

     

A&#34; Java字母或数字&#34;是方法Character.isJavaIdentifierPart(int)返回true的字符。

答案 1 :(得分:5)

我认为这与您所寻找的类似:

true打印java.util.HashSet

false java.lang.Object。{/ p>

public class TestGenerics
{
    public static boolean isGenericType(String s) throws ClassNotFoundException
    {
        Class c = Class.forName(s);
        return c.getTypeParameters().length > 0;
    }

    public static void main(String[] args) throws ClassNotFoundException 
    {
        System.out.println(isGenericType("java.util.HashSet"));
        System.out.println(isGenericType("java.lang.Object"));
    }
}

答案 2 :(得分:5)

这将按对象,类或类名进行测试。

更新:根据有用的评论对此代码进行了多次修订,提供了一些有趣的测试用例。但是,最终问题的适当解决方案取决于问题的确定方式。请参阅早期的修订和评论。

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 { }
}

运行上述代码的结果:

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

答案 3 :(得分:3)

其他答案似乎集中在使用正则表达式或反射,但这似乎要求的是一个解析器。这就是我建议你使用的。

作为一个例子,Eclipse有他们的Java开发工具(JDT)插件,它为您提供了正确执行此操作所需的所有解析工具。您所要做的就是让JDT为您提供一个启用了绑定的抽象语法树(AST)。每当声明一个变量时,你会得到一个声明的变量类型的ITypeBinding,另一个用于实例化的类型(如果变量在声明中被实例化)。

并且ITypeBinding有方法告诉您它是否是通用的,参数化的等等。

如果需要,您还可以获取类型参数。

还有其他java解析器选项,但这是我熟悉的选项。

=============================================

使用Eclipse JDT的特定结果

案例1:HashSet<String> h1 = new HashSet();

案例2:HashSet<String> h2 = new HashSet<>();

案例3:HashSet<String> h3 = new HashSet<String>();

如目前所理解的,本练习的目标是将案例1识别为非通用(它是原始的),将案例2和案例3识别为通用(它们具有类型参数,但在案例2中隐含了类型参数)。

仅查看new HashSet...

生成的节点

所有三种情况都会产生一个ClassInstanceCreation实例。

案例1和案例2有0个类型参数,而案例3有1个类型参数。

案例1的ITypeBinding标记为raw,而对于案例2和3,它们标记为参数化。 这构成了成功,因为我们有办法区分案例1和案例2和案例3。

案例1的ITypeBinding有0个类型参数,而案例2和3在ITypeBinding中都有1个类型参数。请注意,Case 2在AST本身中有0个类型参数,但绑定有一个类型参数。这允许人们区分显式类型参数和菱形运算符的使用。

完成此任务所需的所有信息都可以从AST和绑定中获得。

现在这里有个坏消息:绑定仅在字节代码可用时才可用,即这不是一个简单的文本分析练习。