Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static generic methods

Tags:

java

generics

Can you explain why the following works?

public class GenericsTest<T> {

    public void doSomething(T v1, T v2) {

    }

    public static <T> void doSomethingStatic(T v1, T v2) {

    }

    public static <T> void doSomethingStaticList(List<T> v1, List<T> v2)
    {

    }

    public static void main(String[] args) {
        GenericsTest<String> gt = new GenericsTest<>();

        // OK
        gt.doSomething("abc", "abc");

        // Not OK
        gt.doSomething(1, "abc");

        // OK
        doSomethingStatic(1, 2);

        // Still OK
        doSomethingStatic(1, "abc");

        // So why is this not OK?
        List<String> list1=new LinkedList<>();
        List<Integer> list2=new LinkedList<>();
        doSomethingStaticList(list1,list2);
    }
}

T v1, T v2 should be the same type in doSomethingStatic, but I'm still able to pass different types(integer and string).

If doSomethingStatic() takes a common super class by default, why doesn't doSomethingStaticList() work with different types?

like image 308
Tony Avatar asked Oct 04 '16 10:10

Tony


People also ask

Why generic methods are static?

This is because, the static method can be called without even instantiating the Class. So if the Class is not yet instantiated, we do not yet know what is T. This is the reason why the static methods needs to have its own generics.

Can generic variables be static?

Using generics, type parameters are not allowed to be static. As static variable is shared among object so compiler can not determine which type to used. Consider the following example if static type parameters were allowed.

What is static function in Java?

A static method in Java is a method that is part of a class rather than an instance of that class. Every instance of a class has access to the method. Static methods have access to class variables (static variables) without using the class's object (instance). Only static data may be accessed by a static method.


Video Answer


3 Answers

In non-static case you define T as String when you create instance of GenericsTest. Hence passing an int will give compile error. If you did gt.doSomething(1, 2) it would fail as well.

In static case, you don't define T manually, it is derived from parameters. It will be the first common superclass of both classes - which in this case is Object. You might want to use bounded wildcard, e.g. <T extends Number> or <T extends CharSequence>.

Note that you have two different Ts here:

  • GenericsTest<T>
  • public static <T> void doSomethingStatic(T v1, T v2)

The declaration of generic parameter is whenever you write <T>. You can use different letters in this case to avoid confusion.

like image 148
Jaroslaw Pawlak Avatar answered Oct 26 '22 12:10

Jaroslaw Pawlak


This works because T in your static method is his own type parameter, not the T parameter for instance that used in your member method. Rename it to clarify:

public static class GenericsTest<T> {

    public void doSomething(T v1, T v2) {

    }

    public static <V> void doSomethingStatic(V v1, V v2) {

    }
//...

So in case of doSomething(...) your instance type parameter value is String so it's an error. In case of static doSomethingStatic(...) value of type parameter is different:

GenericsTest.doSomethingStatic(1, "abc"); //ok
GenericsTest.<Object>doSomethingStatic(1, "abc"); //ok
GenericsTest.<String>doSomethingStatic(1, "abc"); //not ok
new GenericsTest<String>().doSomething(1, "abc"); //not ok
like image 20
Philip Voronov Avatar answered Oct 26 '22 12:10

Philip Voronov


First a bit of theory:

  • Generic methods are methods that introduce their own type parameters Java Tutorials - Generic Methods
  • Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable Java Tutorials - Type inference

So what happen:

  • when a generic expression precede the return value then a new generic type variable is "declared". So the T of the class declaration is different (for the compiler) from the T of the method declaration.
  • the compiler apply type inference and in your example it determine that the suitable type to apply method invocation is Object

You can try also this example without static method:

public class GenericsTest<T> {

  public void doSomething(T v1, T v2) {

  }

  public <T> void doSomething2(T v1, T v2) {

  }

  public static void main(String[] args) {
    GenericsTest<String> gt = new GenericsTest<>();

    // ok
    gt.doSomething("abc", "abc");

    // Not ok
    gt.doSomething(1, "abc");

    // ok
    gt.doSomething2(1, 2);

    // Still ok
    gt.doSomething2(1, "abc");

  }

}
like image 2
Aris2World Avatar answered Oct 26 '22 12:10

Aris2World