1
public class A<T> {
    public <K> void m(A<K> target) {
        // determine if T equals K
    }
}

Is it possible to check if <T> and <K> are the same types?

Sergey Metlov
  • 25,747
  • 28
  • 93
  • 153
  • IIRC, type information is erased at runtime (for backwards compatibility with non-generic code). As such, I'm pretty sure the answer is no. – jpm Mar 29 '12 at 18:55
  • 2
    This is not a proper use of generics – ControlAltDel Mar 29 '12 at 18:56
  • I'm pretty sure that it is one of the possible and normal cases of using generics. – Sergey Metlov Mar 29 '12 at 18:59
  • 3
    Type erasure in Java's generics can be befuddling for .Net folks, [perhaps this is the duplicate you are looking for?](http://stackoverflow.com/questions/1004022/java-generic-class-determine-type) – user7116 Mar 29 '12 at 19:04

5 Answers5

6

Yes, this works the same way as the generic TypeReference. Normally types are erased but it works with anonymous inner classes:

public abstract class A<T> {

    private Type type;

    public A() {
        Type superclass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
    }

    public <K> void m(A<K> target) {
            System.out.println( type.equals( target.type ) );
    }
}

To use it:

    A<String> as = new A<String>(){};
    A<String> a2s = new A<String>(){};
    A<Integer> ai = new A<Integer>(){};

    as.m(a2s); // prints true
    as.m(ai);  // prints false

The class does not have to be abstract but it serves as a reminder to make it anonymous inner class. The only real downside is that you have to put {} in the end.

Andrejs
  • 26,885
  • 12
  • 107
  • 96
  • Bonus points for your evil hack with abstract classes -- but OP, there is not normally any way to do this, and there's definitely no way to do it without the "anonymous internal class" hack. – Louis Wasserman Mar 29 '12 at 19:30
  • Yepp, they do have to be anonymous internal classes, updated my answer – Andrejs Mar 29 '12 at 19:51
2

It's not easy because javas type erasure. At runtime the type information for T and K is lost.

But if you can get an instance of those types you can check those at compile time:

public class A<T> {

    private T t;

    public T getT() { return t; }

    public A(T t) {
        this.t = t;
    }

    public <K> m(A<K> target) {
        // determine if T equals K
        boolean areEqual = t.getClass().equals(target.getT().getClass());
    }
}

However that means you need access to instances of the objects.

david
  • 852
  • 1
  • 7
  • 22
1

No, the closest you can come is:

public class A<T> {
  private Class<T> _type;

  public A(Class<T> type) {
    assert type != null;
    _type = type;
  }

  public <K> void m(A<K> target) {
    if (target != null && target._type == this._type) {
      ...
    }
  }

You can see the idiom of passing Class<T> as a constructor parameter in types like EnumMap.

Dilum Ranatunga
  • 13,254
  • 3
  • 41
  • 52
0

Not really, only if you have an instance of T or the Class passed into either A or the method that wants to compare them. Type-erasure...

Kevin Welker
  • 7,719
  • 1
  • 40
  • 56
0

Here's code I wrote that will tell you the generic types of a class. Use this to compare the generic types of A against target.getClass():

/**
 * Gets a list of type Class that contains the type arguments that a child class has used to extend a generic base
 * class.
 * 
 * For example, if a class called ChildClass has this signature:
 * 
 * <code>
 *  public class ChildClass extends ParentClass<Integer, String> 
 * </code>
 * 
 * then the list returned would have two entries: Integer and String.
 * 
 * @param baseClass The generic base class being extended.
 * @param childClass The child class that is doing the extending.
 * @return A list of type Class containing the raw classes for the type arguments.
 */
public <T> List<Class<?>> getTypeArguments(Class<T> baseClass, Class<? extends T> childClass) {

    Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
    Type type = childClass;

    // start walking up the inheritance hierarchy until we hit baseClass
    while (getClass(type) != null && !getClass(type).equals(baseClass)) {
        if (type instanceof Class) {
            // there is no useful information for us in raw types, so just keep going.
            type = ((Class<?>) type).getGenericSuperclass();
        } else {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Class<?> rawType = (Class<?>) parameterizedType.getRawType();

            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
            for (int i = 0; i < actualTypeArguments.length; i++) {
                resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
            }

            if (!rawType.equals(baseClass)) {
                type = rawType.getGenericSuperclass();
            }
        }
    }

    // finally, for each actual type argument provided to baseClass, determine (if possible) the raw class for that
    // type argument
    Type[] actualTypeArguments;
    if (type instanceof Class) {
        actualTypeArguments = ((Class<?>) type).getTypeParameters();
    } else {
        actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
    }

    // convert types to their raw classes
    List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
    for (Type baseType : actualTypeArguments) {
        while (resolvedTypes.containsKey(baseType)) {
            baseType = resolvedTypes.get(baseType);
        }
        typeArgumentsAsClasses.add(getClass(baseType));
    }

    return typeArgumentsAsClasses;

}

/**
 * Gets the Class for a Type. If the Type is a variable type, null is returned.
 * 
 * @param type The Type to get the Class for
 * @return Returns the Class, unless Type is a variable type, then null is returned.
 */
public Class<?> getClass(Type type) {

    Class<?> returnClass = null;

    if (type instanceof Class) {
        returnClass = (Class<?>) type;
    } else if (type instanceof ParameterizedType) {
        returnClass = getClass(((ParameterizedType) type).getRawType());
    } else if (type instanceof GenericArrayType) {
        Class<?> componentClass = getClass(((GenericArrayType) type).getGenericComponentType());
        if (componentClass != null) {
            returnClass = Array.newInstance(componentClass, 0).getClass();
        }
    }

    return returnClass;

}
robertvoliva
  • 902
  • 6
  • 6