218

In Java, I'd like to have something as:

class Clazz<T> {
  static void doIt(T object) {
    // ...
  }
}

But I get

Cannot make a static reference to the non-static type T

I don't understand generics beyond the basic uses and thus can't make much sense of that. It doesn't help that I wasn't able to find much info on the internet about the subject.

Could someone clarify if such use is possible, by a similar manner? Also, why was my original attempt unsuccessful?

Maroun
  • 94,125
  • 30
  • 188
  • 241
André Chalella
  • 13,788
  • 10
  • 54
  • 62

12 Answers12

311

You can't use a class's generic type parameters in static methods or static fields. The class's type parameters are only in scope for instance methods and instance fields. For static fields and static methods, they are shared among all instances of the class, even instances of different type parameters, so obviously they cannot depend on a particular type parameter.

It doesn't seem like your problem should require using the class's type parameter. If you describe what you are trying to do in more detail, maybe we can help you find a better way to do it.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • 11
    Upvoted, this answer actually explains the poster's problem instead of just providing a workaround. – Jorn Jun 01 '09 at 23:20
  • 2
    I thought there would be a whole differente class for each type parameter, so Clazz.doIt() wouldn't be the same method as Clazz.doIt(). I guess I was wrong. Really need to read something more in depth about generics. – André Chalella Jun 02 '09 at 20:05
  • 7
    @Andre: Your intuition is not unfounded; C# does indeed treat generics this way. – jyoungdev Nov 26 '10 at 14:25
  • 39
    "For static fields and static methods, they are shared among all instances of the class, even instances of different type parameters..." Ouch! Kicked in the nuts by type erasure again! – BD at Rivenhill Apr 13 '11 at 18:26
  • 3
    if you will look how generic class/methods looks after compilation, you will see that generic attribute is removed. And List after compilation looks like "List". So there's no different between List and List after compilation - both became List. – Dainius May 28 '12 at 09:45
  • 3
    I think he explained what he is trying to do fairly well. It is clear he is trying to shake dat booty! hah – astryk Apr 28 '15 at 15:47
  • If it's actually implemented in a subclass which defines the type there should be no reason why this can't work, though. And yet, it doesn't. I really hate java generics. You can't even get the class of a generic type. – Nyerguds Feb 08 '16 at 12:54
  • "so obviously they cannot depend on a particular type parameter." - not obvious at all, care to explain? Why can't you use the static method throughout the world with different types, if the type is generic? What does "shared" even mean in this case? Why can't you pass different types in different calls to a static generic method, that's the whole purpose of generics? Why would Java "remember" what you called it with and prevent you from calling it again with a different type for the generic? That's the whole point. – StackLloyd Feb 07 '22 at 09:28
  • @StackLloyd: An instance method refers to the class's type argument because that is the type argument of the instance that the instance method is operating on. A static method does not operate on an instance, so there is obviously no "type argument of the instance". Of course, a static method or instance method can also be generic on the type of some argument , but that would be a type parameter declared for that method (making the method a "generic method"), separate from the class instance's type parameter. – newacct Feb 08 '22 at 07:51
  • @newacct But why would the type only be valid for an instance? Why can't you call MyClass.myGenericStaticMethod()? – StackLloyd Feb 08 '22 at 09:23
  • @StackLloyd: But then the type parameter would be useless inside that method. For an instance method of `Clazz`, `T` has purpose in the instance method only because `this` has type `Class`. For a static method, there is no `this`, so the `T` would have no purpose. Wouldn't your call `MyClass.myGenericStaticMethod()` be identical to `MyClass.myGenericStaticMethod()`? – newacct Feb 09 '22 at 07:11
  • @newacct I fail to understand why would that be. How can the type be useless inside the method, if the reference is the same? Why would you even refer to [this] in a static method? And why would it be identical to a completely different type? Referring to MyClass.myMethod() is exactly why it shouldn't be identical to MyClass. I'm declaring what type I want to use *in that call*, that's the purpose of generics. – StackLloyd Feb 10 '22 at 16:32
  • @StackLloyd: If you have a method, and you want it to be generic on some types in the parameters for example, you would declare type parameters for that method, making it a "generic method". So say you have the class in the question, `Clazz`. You can declare your method like `static U myGenericStaticMethod(U foo, List bar, Clazz ban)`. You declare your method to work the same no matter what U and V are. T is not involved here. You can even declare type parameters for an instance method, if those are independent of the type of `this`. – newacct Feb 11 '22 at 03:43
  • @StackLloyd: In an instance method, the only reason why `T` would be used is in relation to the type of `this` -- `this` has type `Clazz`. So if you have parameters that must match the type argument of the type of `this`, then you would use T. For example, ` T myGenericInstanceMethod(T foo, List bar, List baz)`. Here, T is constrained to be the type such that `this` is type `Clazz`, so it gives you guarantees on the relationship between the types of `foo` and `bar` and `this`, while U is still independent of T. Static methods don't have `this`, so there is no meaning for T. – newacct Feb 11 '22 at 03:47
  • @newacct I understand that there is no way for you to use a static a method which uses T, as in Clazz, inside it. But that's what bothers me. Why is it bound to the instance? "There is no meaning for T" in static methods - why would that be? Why shouldn't you be able to use T inside static methods, changing it as you please by calling Clazz.static() or Clazz.static()? I don't understand why T would have "no purpose" without [this]. – StackLloyd Feb 14 '22 at 14:01
  • @StackLloyd: Like I said, any method (static or instance) can declare type parameters of its own, which would be unrelated to the type variable `T`. Your method `static()` can declare its own type parameter (say `U` to avoid confusion with `T`), and you can just call it as `Clazz.static()` and the type variable would be inferred from the argument types or return type. – newacct Feb 21 '22 at 07:45
158

Java doesn't know what T is until you instantiate a type.

Maybe you can execute static methods by calling Clazz<T>.doit(something) but it sounds like you can't.

The other way to handle things is to put the type parameter in the method itself:

static <U> void doIt(U object)

which doesn't get you the right restriction on U, but it's better than nothing....

Andreas Fester
  • 36,091
  • 7
  • 95
  • 123
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • Then I'd call the method without specifying any constraints, ie Clazz.doIt(object) instead of Clazz.doIt(object), right? Do you consider that OK? – André Chalella Jun 01 '09 at 19:45
  • The second syntax there is the more exact one, which you need if the compiler can't infer the type of the return value from the contaxt in which the method is called. In other words, if the compiler allows Clazz.doIt(object), then do that. – skaffman Jun 01 '09 at 19:57
  • 2
    I tried Clazz.doIt(object) and got a compile-time error! "Syntax error on token(s), misplaced construct(s)". Clazz.doIt(object) works fine though, not even a warning. – André Chalella Jun 01 '09 at 20:14
  • 10
    Use Clazz.doIt(object). I think it's weird syntax, comparing to C++'s Clazz::doIt(1) – Reci Mar 01 '12 at 02:17
  • Why do you say it doesn't get you the right restriction on U? – Adam Burley May 27 '15 at 11:13
  • @CrendKing I wouldn't call it weird. If your method is defined as `static void some(U u)`, then if you say `A.some("")`, you are saying "execute the method `some` with type parameter `String` in class `A`". On the other hand, `A.some("")` says "execute the method `some` on the class `A` which has a type parameter of type `String`". Which does not make sense, because you don't have generic class refinement in Java. (it's partially being planned in Project Valhalla though) – Adowrath Mar 08 '17 at 16:47
55

I ran into this same problem. I found my answer by downloading the source code for Collections.sort in the java framework. The answer I used was to put the <T> generic in the method, not in the class definition.

So this worked:

public class QuickSortArray  {
    public static <T extends Comparable> void quickSort(T[] array, int bottom, int top){
//do it
}

}

Of course, after reading the answers above I realized that this would be an acceptable alternative without using a generic class:

public static void quickSort(Comparable[] array, int bottom, int top){
//do it
}
Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
Chris
  • 551
  • 4
  • 2
  • 10
    While the accepted answer was technically correct: this is this is actually what I was looking for when Google led me to this question. – Jeremy List Oct 16 '13 at 03:29
  • Props for providing an example that includes the `T extends XXX` syntax. – Ogre Psalm33 Nov 18 '14 at 20:25
  • 3
    @Chris Since you're using generics anyway, you might as well use them all the way --- namely not to use raw types like `Comparable`. Try `>` instead. – easoncxz Jul 10 '15 at 13:05
24

I think this syntax has not been mentioned yet (in case you want a method without arguments):

class Clazz {
  static <T> T doIt() {
    // shake that booty
  }
}

And the call:

String str = Clazz.<String>doIt();
Pang
  • 9,564
  • 146
  • 81
  • 122
Oreste Viron
  • 3,592
  • 3
  • 22
  • 34
  • 1
    Actually (I don't know the Java version), this is possible even without the `` because it simply infers the type argument because you assign it to a variable. If you don't assign it, it simply infers `Object`. – Adowrath Mar 08 '17 at 17:00
  • This is a perfect solution. – Prabhat Ranjan Aug 12 '19 at 08:41
16

It is possible to do what you want by using the syntax for generic methods when declaring your doIt() method (notice the addition of <T> between static and void in the method signature of doIt()):

class Clazz<T> {
  static <T> void doIt(T object) {
    // shake that booty
  }
}

I got Eclipse editor to accept the above code without the Cannot make a static reference to the non-static type T error and then expanded it to the following working program (complete with somewhat age-appropriate cultural reference):

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }

  private static class KC {
  }

  private static class SunshineBand {
  }

  public static void main(String args[]) {
    KC kc = new KC();
    SunshineBand sunshineBand = new SunshineBand();
    Clazz.doIt(kc);
    Clazz.doIt(sunshineBand);
  }
}

Which prints these lines to the console when I run it:

shake that booty 'class com.eclipseoptions.datamanager.Clazz$KC' !!!
shake that booty 'class com.eclipseoptions.datamanager.Clazz$SunshineBand' !!!

BD at Rivenhill
  • 12,395
  • 10
  • 46
  • 49
  • In this case, does the second hide the first one? – André Chalella May 14 '12 at 08:47
  • 1
    @AndréNeves, Yes. The second `` masks the first one in the same way that in `class C { int x; C(int x) { ... } }` the parameter `x` masks the field `x`. – Mike Samuel May 24 '12 at 14:58
  • How to explain the output of the sample : it seems that there is indeed two types involved, one by parameter T value? If T was really hiding the class type I would expect "shake that booty 'class com.eclipseoptions.datamanager.Clazz" outputted twice without parameters. – Pragmateek Dec 29 '12 at 18:36
  • 1
    It has nothing to do with masking. A static method simply can't be bound by **the classes generic type**. Nevertheless it can define its own generic types and boundaries. – mike Aug 09 '13 at 11:08
7

It is correctly mentioned in the error: you cannot make a static reference to non-static type T. The reason is the type parameter T can be replaced by any of the type argument e.g. Clazz<String> or Clazz<integer> etc. But static fields/methods are shared by all non-static objects of the class.

The following excerpt is taken from the doc:

A class's static field is a class-level variable shared by all non-static objects of the class. Hence, static fields of type parameters are not allowed. Consider the following class:

public class MobileDevice<T> {
    private static T os;

    // ...
}

If static fields of type parameters were allowed, then the following code would be confused:

MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();

Because the static field os is shared by phone, pager, and pc, what is the actual type of os? It cannot be Smartphone, Pager, and TabletPC at the same time. You cannot, therefore, create static fields of type parameters.

As rightly pointed out by chris in his answer you need to use type parameter with the method and not with the class in this case. You can write it like:

static <E> void doIt(E object) 
Community
  • 1
  • 1
akhil_mittal
  • 23,309
  • 7
  • 96
  • 95
3

Something like the following would get you closer

class Clazz
{
   public static <U extends Clazz> void doIt(U thing)
   {
   }
}

EDIT: Updated example with more detail

public abstract class Thingo 
{

    public static <U extends Thingo> void doIt(U p_thingo)
    {
        p_thingo.thing();
    }

    protected abstract void thing();

}

class SubThingoOne extends Thingo
{
    @Override
    protected void thing() 
    {
        System.out.println("SubThingoOne");
    }
}

class SubThingoTwo extends Thingo
{

    @Override
    protected void thing() 
    {
        System.out.println("SuThingoTwo");
    }

}

public class ThingoTest 
{

    @Test
    public void test() 
    {
        Thingo t1 = new SubThingoOne();
        Thingo t2 = new SubThingoTwo();

        Thingo.doIt(t1);
        Thingo.doIt(t2);

        // compile error -->  Thingo.doIt(new Object());
    }
}
ekj
  • 1,082
  • 2
  • 11
  • 22
  • Exactly as Jason S suggested. – André Chalella May 10 '12 at 09:50
  • @Andre the previous suggestion did not provide the restriction that U must extend Clazz – ekj May 24 '12 at 10:07
  • I see, but it doesn't add anything useful except for the constraint. In my original post, I would like to be able to do this without passing U as a parameter. – André Chalella May 24 '12 at 16:07
  • @Andre See my update above. The generic type is only defined in the method definition, and does not have to be passed when calling the method – ekj Jun 08 '12 at 09:56
  • @ekj Thank you, I understand now. Sorry, I posed this question three years ago, when I was doing Java daily. I got your idea, even though I think you do understand that this is not exactly what I intended originally. However, I liked your answer. – André Chalella Jun 08 '12 at 13:57
3

Since static variables are shared by all instances of the class. For example if you are having following code

class Class<T> {
  static void doIt(T object) {
    // using T here 
  }
}

T is available only after an instance is created. But static methods can be used even before instances are available. So, Generic type parameters cannot be referenced inside static methods and variables

Bharat
  • 386
  • 5
  • 20
2

When you specify a generic type for your class, JVM know about it only having an instance of your class, not definition. Each definition has only parametrized type.

Generics work like templates in C++, so you should first instantiate your class, then use the function with the type being specified.

Marcin Cylke
  • 2,100
  • 2
  • 21
  • 40
  • 4
    Java generics are quite different from C++ templates. The generic class is compiled by itself. In fact the equivalent code in C++ would work (calling code would look like `Clazz::doIt( 5 )` ) – David Rodríguez - dribeas Jun 01 '09 at 20:06
  • I like this answer better than the accepted one... apart from the C++ template reference, the comment about the generic type only being for one instance of the class instead of the entire class is spot on. The accepted answer doesn't explain this, and just provides a workaround which doesn't have anything to do with why you can't use the class's generic type in a static method. – Jorn Jun 01 '09 at 23:18
  • @DavidRodríguez-dribeas makes valid point. The equivalent C++ code would work. I stumbled on this trying to make it work the C++ way and ofcourse it doesn't. – themagicalyang Jul 17 '22 at 14:42
1

Also to put it in simple terms, it happens because of the "Erasure" property of the generics.Which means that although we define ArrayList<Integer> and ArrayList<String> , at the compile time it stays as two different concrete types but at the runtime the JVM erases generic types and creates only one ArrayList class instead of two classes. So when we define a static type method or anything for a generic, it is shared by all instances of that generic, in my example it is shared by both ArrayList<Integer> and ArrayList<String> .That's why you get the error.A Generic Type Parameter of a Class Is Not Allowed in a Static Context!

1

@BD at Rivenhill: Since this old question has gotten renewed attention last year, let us go on a bit, just for the sake of discussion. The body of your doIt method does not do anything T-specific at all. Here it is:

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

So you can entirely drop all type variables and just code

public class Clazz {
  static void doIt(Object object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

Ok. But let's get back closer to the original problem. The first type variable on the class declaration is redundant. Only the second one on the method is needed. Here we go again, but it is not the final answer, yet:

public class Clazz  {
  static <T extends Saying> void doIt(T object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}
// Output:
// KC
// Sunshine

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}

However, it's all too much fuss about nothing, since the following version works just the same way. All it needs is the interface type on the method parameter. No type variables in sight anywhere. Was that really the original problem?

public class Clazz  {
  static void doIt(Saying object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}
0

T is not in the scope of the static methods and so you can't use T in the static method. You would need to define a different type parameter for the static method. I would write it like this:

class Clazz<T> {

  static <U> void doIt(U object) {
    // ...
  }

}

For example:

public class Tuple<T> {

    private T[] elements;

    public static <E> Tuple<E> of(E ...args){
        if (args.length == 0) 
             return new Tuple<E>();
        return new Tuple<E>(args);
    }

    //other methods
}
Cheng Thao
  • 1,467
  • 1
  • 3
  • 9