5

I have the following method:

public <U, V> boolean isEqual(List<U> a, List<V> b) {
    // check if U == V
}

I want to check if U and V are the same classes.

Tunaki
  • 116,530
  • 39
  • 281
  • 370
Mahmoud Hanafy
  • 1,614
  • 1
  • 22
  • 30
  • 3
    Why do you want this? What's your use-case? Why not create another method having a single type `U`? And what if `U` is `? extends Something`? What does `U == V` even mean? – Tunaki Jan 22 '16 at 16:28
  • 2
    I'm creating a method to test equality for two objects. The first condition should be that their type are the same – Mahmoud Hanafy Jan 22 '16 at 16:32
  • I don't understand the purpose of this. Can you clarify with details like what do you wanna achieve? – We are Borg Jan 22 '16 at 16:36
  • 2
    I want to create method to compare two objects with generic types. If the two generic types are not the same, I want to tell the user this message "your generic types are not the same". Then I will continue to compare the objects of the two methods. – Mahmoud Hanafy Jan 22 '16 at 16:40
  • @Mahmoud why not just take two `List` in your compare method? – Captain Man Jan 22 '16 at 16:54
  • 2
    I want to give the user the flexibility to send two different types, and I want to tell him that they are not equal. – Mahmoud Hanafy Jan 22 '16 at 16:56

6 Answers6

8

You can't do that because of type erasure, it is that simple.

Consider the following:

public static void main(String[] args) {
    List<? extends Number> l1 = Arrays.asList(1L, 2, 3L);
    List<? extends Number> l2 = Arrays.asList(1);
    isEqual(l1, l2);
}

public static <U, V> boolean isEqual(List<U> a, List<V> b) {
    // is U == V here?
}

Is U == V here? l1 contains Long and Integer instances but l2 contains a single Integer instance.


I'm guessing from your comment:

The first condition should be that their type are the same

that what you should have instead is a single type U. In this case, use the following signature:

public static <U> boolean isEqual(List<U> a, List<U> b) {

}

and with that, the above code won't compile anymore.


What you could also do is add 2 parameters accepting the classes:

public static <U, V> boolean isEqual(List<U> a, List<V> b, Class<U> uClass, Class<V> vClass) {
    if (!uClass.equals(vClass)) {
        // classes are different
    }
}

In this case, you can print a message if the classes given are not the same.

Tunaki
  • 116,530
  • 39
  • 281
  • 370
  • I want to give the user the flexibility to send two different types, and I want to tell him that they are not equal. – Mahmoud Hanafy Jan 22 '16 at 16:48
  • 1
    @MahmoudHanafy Unfortunately, this is something you won't be able to do. – Tunaki Jan 22 '16 at 16:50
  • @Mahmoud Rather than giving them the flexibility to send two different types which you are saying is an error that you want to inform them of, why not simply not let them send different types in the first place? – Captain Man Jan 22 '16 at 16:58
2

If you are making your own class you can require that Class<T> be included in the constructor as demonstrated here

Ex:

public class SomeClass<T> {

    private final Class<T> clazz;

    public SomeClass(Class<T> clazz) {
        this.clazz = clazz;
    }

    public Class<T> getParam() {
        return clazz;
    }
}

Now you can call SomeClass#getParam() to get the type param declared.


There is also a way to do this with reflection.


All this said, the reason you have to do weird work-arounds to this is because of Type Erasure. Basically at runtime Java sees all generics as Object, so while compiling your List may be a List<Integer> or List<Boolean>, but at runtime they're both List<Object>.

Community
  • 1
  • 1
Captain Man
  • 5,651
  • 3
  • 41
  • 64
1

If you're trying to compare the contents of two lists, then you shouldn't implement the type comparison yourself. Instead you should do this:

    public static <U, V> boolean isEqual(List<U> a, List<V> b) {
        if (a.size() != b.size()) 
            return false;
        for (int i = 0; i < a.size(); i++)
            if (!a.get(i).equals(b.get(i)))
                return false;
        return true;
    }

This way you're relying on the types U and V to be able to handle equals() properly. Here's some guidelines on implementing equals(): http://www.javaworld.com/article/2072762/java-app-dev/object-equality.html

What I'm guessing you want to do is to be able to return quickly in case the types are different. But with implementation I gave you, you'll get the same behaviour -- you'll return on the first iteration.

MSiddeek
  • 71
  • 7
0

Do not rely on

a.getClass().equals(b.getClass())

This is not correct, it will only check whether both are of type ArrayList and not String, Integer etc.

Try this only

a.get(0).getClass().equals(b.get(0).getClass())

Make sure you check null condition, otherwise you will encounter NullPointerException here.

munish
  • 383
  • 2
  • 17
  • This is comparing the runtime type of the first element of both lists, not the type parameter of the list themselves. – Captain Man Jan 22 '16 at 16:41
0

I think you would like to check whether a == b instead of U == V. In that case you have to write your own compare method to compare the instance of class U and V.

Don
  • 11
  • 2
-1

Assuming a and b are not null and not empty,

a.get(0).getClass().equals(b.get(0).getClass())

should do the trick.

  • How is this different than [this answer](http://stackoverflow.com/a/34951639/1743880)? – Tunaki Jan 22 '16 at 16:45
  • This is comparing the runtime type of the first elements of the lists, not the parameter of the list. Two `List`s will not be equal if one contains an `Integer` first and another contains a `Double`. Further, a `List` and `List` would be equal if they both contained a `Double` first. – Captain Man Jan 22 '16 at 16:51