Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List<T> is not equal to List<T>?

Tags:

java

generics

Have a look at this simple example of Java generics:

class List<T> {
    T head;
    List<T> next;
}

class A<T> {
    List<T> l;

    public <T> int length() {
        List<T> l = this.l;
        int c = 1;
        while (l.next != null) {
            c++;
            l = l.next;
        }
        return c;
    }

    public static void main(String[] args) {
        A<Integer> a = new A<Integer>();
        a.l = new List<Integer>();
        a.l.head = 123;
        a.l.next = new List<Integer>();
        a.l.next.head = 432;
        System.out.println("list length: " + a.length());
    }
}

It gets an error compiling, claiming that the types are incompatible, yet claiming that the two variables are the same type:

$ javac A.java && java A
A.java:10: incompatible types
found   : List<T>
required: List<T>
        List<T> l = this.l;
                        ^
1 error

If I change the first line of length() to List<T> l = (List<T>)(Object)this.l;, it works. Why?

like image 920
Dog Avatar asked Jun 24 '13 18:06

Dog


People also ask

How to compare items of two lists in c#?

Equals(Object) Method which is inherited from the Object class is used to check if a specified List<T> object is equal to another List<T> object or not. Syntax: public virtual bool Equals (object obj);

How to check if a list contains a particular string in c#?

if (myList. Contains(myString)) string element = myList. ElementAt(myList. IndexOf(myString));

Are two empty lists equal?

They are two separate objects. See this answer from the C# FAQ. Show activity on this post. In other words, since these are two different lists, they have different references, so Equals returns false .


1 Answers

You've declared a generic method inside a generic class with this line:

public <T> int length() {

This <T> is different than your class's <T>. According to the JLS Section 6.3:

The scope of a class's type parameter (§8.1.2) is the type parameter section of the class declaration, the type parameter section of any superclass or superinterface of the class declaration, and the class body.

You don't need to re-declare <T> on your method; the class's type parameter is already in scope.

To use your class's generic type parameter <T>, have your method not declare another <T> and simply use your class's <T>:

public int length() {

To understand why your casting works:

List<T> l = (List<T>)(Object)this.l;

You can cast any object to Object. Then you are casting the result to List<T>. You can always cast it to anything you want; Java will simply throw a ClassCastException at runtime if it wasn't really a List at runtime. But the compiler will also give a warning that it uses unchecked or unsafe operations, because it can't guarantee that this <T> is the original <T>.

To illustrate the difference between <T>s, you can use <U> as the generic type parameter for the method and get the same results:

public <U> int length() {
    List<U> l = (List<U>)(Object)this.l;

This compiles with the same type safety warning.

If you really know that type safety can be guaranteed, you can use @SuppressWarnings("unchecked") to annotate your method. But here, I'd still go with removing the generic type parameter from the method entirely, and using the class's type parameter.

like image 85
rgettman Avatar answered Sep 24 '22 20:09

rgettman