Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I statically reference an inner class's static method on a generic class?

Please go through the entire question to have complete idea.

First let the class Box is given as follows:-

public class Box <T>{
    private T t;

    public  void  set(T t){
        this.t  = t;
        System.out.println("value:\n");
        System.out.printf("%s", t.toString());
    }
    public T get() {
        return t;
    }
    static int retInt(){
        return 5;

    }
     public <U extends Number> void inspect(U u){
            System.out.println("T: " + t.getClass().getName());
            System.out.println("U: " + u.getClass().getName());
        }

}

Generic class Util as given below:-

public class Util<T>{
    private T t;

    //Generic method
    public  <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }

    /* Static generic or non-generic methods can be declared in generic class 
       but they can not make use of generic parameter type(as generics static    
       methods  using class type variable must know the type argument 
       (i.e value of type parameter); and knowledge of type argument is
       possible only when object of same generic class are instantiated  
       (meaning assigning value of generic type parameter <T> or better to 
       say declared object have it's type argument; for example 
       as in List<T> replace T with Integer,String, Float etc);
       but static method may be called without having 
       instance of class; so such declaration for static generic method are
       not allowed) here it is <T>;  like for example as shown below  

       public static int checkFun(T t){
          return 5;
       } // this generate compiler error complaining "can not make static
        //  reference to non-static type T".           
    */


    public static <K> boolean cmp(Box<K> b1, Box<K> b2){
        // implement comparator to compare but here 
        return true;
    }

    // Inner class Pair
    public class Pair <K, V> {
        private K key;
        private V value;

        // Generic constructor
        public Pair(K key, V value) {
            this.key = key;
            this.value = value;
        }

        public void setKey(K key) {
            int i = 6;
            if(i >4 || i<9);
            this.key = key;
        }

        public void setValue(V value) {
            this.value = value;
        }

        public K getKey(){
            return key;
        }

        public V getValue(){
            return value;
        }

    }

    public void main1() {           
        //The complete syntax for invoking this method would be:
        // <Integer, String>   new Util<T>().
        Pair<Integer, String> p1 = new Pair<Integer,String>(1, "apple");
        Pair<Integer, String> p2 = new Pair<Integer, String>(2, "pear");
        boolean same = compare(p1, p2);
        //boolean same = true;
        if(same)System.out.println("it is true: they are the same");
        else System.out.println("nah! they are not the same...");

        //boolean sm = compare();
    }

    public static void main  (String[] args) /*throws FileNotFoundException */{
        //new Util<Integer>(). main1();

        Util<Integer> util = new Util<>();
        util.main1();
    }  
}

Above code compiles and executes just fine, my discomfort lies here:

If we add static modifier to method

public <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2)    -------(1)
// called in method main1() 

and make it

public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2)     -------(2) 

then compiler will complain that Cannot make a static reference to the non-static type Pair whereas similar method

 public static <K> boolean cmp(Box<K> b1, Box<K> b2)    -------(3)

which is also static but doesn't complain. Even though we are not using type parameter <T> in both method but a big but in method we are talking from eq-1 the argument it uses is from inner class Pair (so reasoning of my ambiguity may be explained with reference to this feature).

But still; logically, I feel that adding modifier static to method in eq-1 should not generate compile time error because wherever the method in eq-2 is called it will be responsibility of that method to call with correct argument to method in eq-2 and it should be allowed to be called like a static method.

Question:- What is the explanation for not using static modifier for the method:

public <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2)

Thanks for the help.

like image 376
zeal Avatar asked May 23 '14 14:05

zeal


1 Answers

The reason for this is the type parameter on the Util class and the inner class Pair is non-static. Because Pair is a non-static inner class, it can use the T type parameter of Util (even though in this case it does not). Therefore when Pair is used it is necessary to specify a T, either by accessing it within the context of a Util instance (where the T is implicitly available) or by qualifying by accessing it through a specific Util<T>, e.g. Util<Integer>.Pair<String,Object>. Another way to think of it is that the type of Pair depends on the type parameter of Util and Util<String>.Pair<K,V> is not compatible with Util<Object>.Pair<K,V>.

To keep the type parameter T on Util and keep Pair as a non-static inner class, you can change the signature of compare to

public static <T, K, V> boolean compare(Util<T>.Pair<K, V> p1, Util<T>.Pair<K, V> p2)

or

public static <K,V> boolean compare(Util<?>.Pair<K,V> p1, Util<?>.Pair<K,V> p2)

which is possible because the instantiation of T is not relevant to the body of the method.

As an alternative, since Pair does not refer to anything (non-static) inside of Util you can change the definition of Pair to be

public static class Pair <K, V> { /* ... */ }

Finally for completeness, if Util did not have a type parameter then

public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2)

and

public class Pair<K, V> { /* ... */ }

would be just fine.

like image 74
Geoff Reedy Avatar answered Oct 05 '22 03:10

Geoff Reedy