Search code examples
javagenericscollectionscomparable

How does Java's generics work?


Could anyone please help me understand how does Java's generics work? I understand the concept of it. But for this specific example of the code I don't clearly understand the compiler's error messages.

Example code: Test class

// Test code 
public class A < ListType extends Comparable < ListType >> {
// To make the instance variable y
public int y;
// To make the instance variable s
public String s;

//Constructor Method
public A(int requiredY, String requiredS) {
y = requiredY;
s = requiredS;
}

more code here...
}

Then in a different class I wrote

List <A> a = new ArrayList<A>();
more code here...
Collections.sort(a)

The error message I am getting is

test.java:20: error: no suitable method found for sort(List<A>)
Collections.sort(a);
           ^
method Collections.<T#1>sort(List<T#1>) is not applicable
  (inference variable T#1 has incompatible bounds
    equality constraints: A
    upper bounds: Comparable<? super T#1>)
method Collections.<T#2>sort(List<T#2>,Comparator<? super T#2>) is not applicable
  (cannot infer type-variable(s) T#2
    (actual and formal argument lists differ in length))

where T#1,T#2 are type-variables:

T#1 extends Comparable<? super T#1> declared in method <T#1>sort(List<T#1>)
T#2 extends Object declared in method <T#2>sort(List<T#2>,Comparator<? super T#2>)

I don't understand why is the compiler complaining about the type parameter. Shouldn't the collections work? Because the type parameters are both mutually comparable.


Solution

  • Either you're writing your question wrong in order to hide the class names, or you're mistaken in representing the generics.

    If what you're trying to do is making a class that could be sorted, you can implement Comparable in the class A as others have suggested.

    public class A < ListType extends Comparable < ListType >> {
    ...
    }
    

    The above code would require the class A to accept a class that extends/implements Comparable, and use ListType as its type erasure. Since you don't show how would you use the ListType to bound a type, I don't think this is what you want.

    Usually generics are used to bound the type of parameter you can use in a class, in order to provide a type-safe operations in compile time.

    import java.lang.Override;
    public class A <ListType extends Comparable<ListType>>{
        ListType lt;
        A(ListType b){
            this.lt = b;
        }
        static class B implements Comparable<B>{
            B(){};
            @Override
            public int compareTo(B b){
                return 0;
            }
        }
        static class C implements Comparable<B>{
            C(){};
            @Override
            public int compareTo(B c){
                return 0;
            }
        }
    
    
        public static void main(String[] args){
            A<B> a = new A<B>(new B());   //OK
            A<C> _a = new A<C>(new C()); //ERROR: is not within bound
            System.out.println("");
        }
    }
    

    Because class C is not implementing a Comparable class with itself, you cannot pass a class C variable to the class A constructor. If you want to create a type that will accept any classes that extends Comparable, you could use a wildcard ?.

    public class A <ListType extends Comparable<?>>
    

    or use a single capital letter as type for better code styling

    public class A <T extends Comparable<?>>