how to compare type of an object's instance to a generic type?

22,635

Solution 1

Basically you can't do that due to type erasure. The normal workaround is to pass a Class object as a parameter; e.g.

    public <U extends IComponent> U GetComponent(Class<U> clazz) {
        for (IComponent component : list) {
            if (clazz.isInstance(component)) {
                return clazz.cast(component);
            }
        }
    }

You could also use if (clazz.equals(component.getClass())) { ... but that does an exact type match ... which is not what the instanceof operator does. The instanceof operator and the Class.instanceOf method both test to see if the value's type is assignment compatible.

Solution 2

As far as I know, you can't. You'll have to take a Class object as a parameter:

public <U extends IComponent> U getComponent(Class<U> clazz) {
    // ...
    if (component.getClass() == clazz) {
        return (U) component;
    }
}

And call it like this:

getComponent(MyComponentImpl.class);

Solution 3

General rule with java - if a generic class needs to know its generic type(s), then you have to pass a hint. A common approach for the problem is using the constructor:

public class ComponentsManager<U extends IComponent> {
  private Class<U extends IComponentManeger> genericType = null;
  public ComponentsManager<U extends IComponent>(
                       Class<U extends IComponent> genericType) {
    this.genericType = genericType;
  }

}

Now the class knows it's generic type class and you can uses the generic type class instance to verify, if a component in your collection matches the classes generic type.

Solution 4

You could try adding a couple of 'test' methods:

private static <U extends IComponent> boolean testComponent(U u) {
  return true;
}

private static boolean testComponent(Object o) {
  return false;
}

Then, use testComponent(component) instead of component instanceof U.

Example code:

import java.util.*;

class IComponent {
}

class T1 extends IComponent {
}

public class Test {

  public static <U extends IComponent> boolean testComponent(U u) {
    return true;
  }

  public static boolean testComponent(Object o) {
    return false;
  }

  public static void main(String[] args) {
    T1 t = new T1();
    System.out.println("hm? " + (testComponent(t) ? "true" : "false"));
  }
}

Output:

hm? true

Share:
22,635
Adibe7
Author by

Adibe7

Updated on July 09, 2022

Comments

  • Adibe7
    Adibe7 almost 2 years

    how can i write this code in java?

    public class ComponentsManager 
        {
            private List<IComponent> list = new ArrayList<IComponent>();
    
            public <U extends IComponent> U GetComponent() {
    
                for (IComponent component : list) {
    
                    if(component instanceof U)
                    {
                        return component;
                    }
                }
            }
    }
    

    but i cant perform instanceof on generic types. how should i do it? thanks.

  • Hosam Aly
    Hosam Aly almost 13 years
    You're right @Stephen. I forgot to cast the result. Thanks for pointing it out.
  • sje397
    sje397 almost 13 years
    @Steven C: Would you care to comment on my answer? Everyone says 'can't' yet my little demo works..
  • Stephen C
    Stephen C almost 13 years
    that won't compile either. Try it and see.
  • Stephen C
    Stephen C almost 13 years
    (Since @sje397 asked me to comment ...): This will "work", but you have to do an unsafe type conversion inside the GetComponent method ... or change it to a non-generic method. In addition, you are duplicating Java's type checking by adding your own ersatz type checking. This is cumbersome, and the end solution is more fragile than a solution that uses real Java type checking.
  • Hosam Aly
    Hosam Aly almost 13 years
    @Stephen: I tried it and it works (at least in Eclipse using JDK 6). It compiles with an "Unchecked cast" warning, but it works fine. What error do you get?
  • Stephen C
    Stephen C almost 13 years
    that's what I mean. Most people consider those to be anathema. The problem is that while it is safe in practice, a small change will result in it being unsafe. For this reason, it is a bad idea to ignore or suppress it. Instead you should treat it as a hard compilation error and fix it.
  • WestCoastProjects
    WestCoastProjects over 10 years
    Even given Stephen's comments, this is useful. Oh wait, this does not seem to compile "identical type erasure". @sje397: is this working for you? Apparently you have to include the methods within EACH class? that is not so useful then ..
  • ToolmakerSteve
    ToolmakerSteve over 8 years
    All this "answer" does is demonstrate that you can direct objects that extend IComponent to a particular method. The problem is that once you are in that method, type erasure means you don't know that it is a "U", so you can't do any reasoning based on "U" -- which is what is needed to address the question. All that is known inside the method "testComponent", is that "u" is an IComponent. A better test would have been to pass two parameters, "U u" and "IComponent other", then show that you could determine whether "other" is a "U". But at that point, is better to pass in the desired class.
  • ToolmakerSteve
    ToolmakerSteve over 8 years
    Or to put it another way: of course it returns true That's merely a demonstration of overloading; a non-generic method with parameter IComponent would have the exact same behaviour. Now prove that the "u extends IComponent" gains you anything. I don't think it does, but even if I am wrong, passing in a Class would be a cleaner way to accomplish the result.
  • Chexxor
    Chexxor almost 8 years
    Why wouldn't Oracle/Java add the possibility for a generic type to access the .class field(/method/identifier)? Am I right to assume that the reason relates to what happens compile time vs runtime?
  • Stephen C
    Stephen C almost 8 years
    The fundamental reason for this is type erasure. The fundamental reason for type erasure is that generic types needed to be implemented as a compile time mechanism (in Java 5) for compatibility with earlier versions of Java.
  • Manish Chandra Joshi
    Manish Chandra Joshi over 4 years
    The name "clazz" of the parameter kind of weirds me out. But it seems that almost every example of this on SO uses the exact same name.
  • Stephen C
    Stephen C over 4 years
    Try using class instead and see what happens. (Then turn down your "weirdness sensor".)