4

I learned yesterday that you can invoke a static method on a type parameter. For example:

public static <T extends Arrays> void main(String[] args) {
    T.sort(args);
}

Is there any good reason for allowing this? Could it perhaps have helped with the transition from non-generic to generic code in some way?

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • 2
    No, there's not a good reason for allowing this; it _won't_ work the way you think (it will _always_ call the static method on the upper bound of the type; e.g. even if `T` extended `Arrays`, you'd still get `Arrays.sort` here.) – Louis Wasserman Mar 20 '16 at 02:08
  • I'm aware of this (http://stackoverflow.com/a/36095702/3973077). Given that it's completely useless I wonder why Sun bothered? It's just bizarre. – Paul Boddington Mar 20 '16 at 02:17
  • @LouisWasserman is correct. This code is identical to `Arrays.sort(args)`. Static methods can not be overridden by subclasses. Do not try this at home. – Bohemian Mar 20 '16 at 02:17
  • [Simple test case](http://ideone.com/92YflP) showing a result of this. – Obicere Mar 20 '16 at 02:20
  • It's similar to [calling static functions from objects](http://stackoverflow.com/a/610674/2398375): it's one of those strange things that only Sun could answer, and we can only make assumpsions about. – Vince Mar 20 '16 at 02:23

1 Answers1

3

I don't think it is something that was explicitly allowed, but simply that it wasn't disallowed.

JLS 8.4.4 Generic Methods doesn't specify any disallowed use of a type variables.

JLS 8.1.2 Generic Classes and Type Parameters lists where T cannot be used:

It is a compile-time error to refer to a type parameter of a generic class C in any of the following:

  • the declaration of a static member of C (§8.3.1.1, §8.4.3.2, §8.5.1).
  • the declaration of a static member of any type declaration nested within C.
  • a static initializer of C (§8.7), or
  • a static initializer of any class declaration nested within C.

Basically, a type variable is allowed anywhere a type is allowed, unless otherwise stated, and they didn't list static method call.

Deliberate or oversight? Who knows, but probably deliberate. Since it is allowed to call static methods on an instance variable, why not allow this too. Just because it's allowed, doesn't mean you should ever do it.

As JLS 4.4 Type Variables says it (emphasis mine):

A type variable is an unqualified identifier used as a type in class, interface, method, and constructor bodies.

Community
  • 1
  • 1
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • I'm trying to find where `T.class` explicitly excluded. – Paul Boddington Mar 20 '16 at 02:29
  • I don't really see how this is relevant to the question. 8.4.4 discusses the definition of a generic method. 8.1.2 discusses the usage of a type parameter (for a class) and how it can't [be used in a static context](http://stackoverflow.com/questions/936377/static-method-in-a-generic-class). And 4.4 is just a definition of a type variable which is completely independent of the question. None of those articles are even related to the question. – Obicere Mar 20 '16 at 02:30
  • 2
    @PaulBoddington [JLS 15.8.2 Class Literals](https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.8.2): *It is a compile-time error if the named type is a type variable* – Andreas Mar 20 '16 at 02:34
  • @Andreas Obviously I wasn't trying very hard! Thanks +1 – Paul Boddington Mar 20 '16 at 02:35
  • @Obicere It is an answer to the question of why it is allowed. Short version: Type variables are allowed anyway a type is allowed, unless otherwise stated (explicitly disallowed). Whether it is with good reason is good opinionated a discussion to go into. – Andreas Mar 20 '16 at 02:35
  • @Andreas You're mixing up terms quite a bit here. It is wrong to assume that type variables and type parameters are the same. You also failed to reference arguably the only real mention to this behavior in the JLS (8.4.3.2) which states: `A class method is always invoked without reference to a particular object.` Which highlights the fact that static methods in `T` will not be called in place of the defined methods. – Obicere Mar 20 '16 at 02:37
  • And to clarify the last sentence. The implication of the question is that because `static` invocation is allowed on an instance (8.4.3.2), wouldn't it also be allowed on an inferred type. The answer to both of these is solved the same way by resolving the actual type. This would be the type of the instance and the upperbound in each case, respectively. Aside from that, there is no further mention of this in the JLS AFAIK. If there were to be, it would be under the type inference section. – Obicere Mar 20 '16 at 02:46
  • @Obicere *"without reference to a particular object"* means that the call will not have a `this` reference, and has nothing to do with this. If I have `class A` and `class B extends A`, and both declare `static void foo()`, the code `A x = new B(); x.foo();` will call `A.foo()` even though `x` is a reference to `B`, because `foo()` is resolved at compile-time to the declared type of `x`. The exact same thing applies to `T`, where the *compiler* resolves `T.sort()` to `Arrays.sort()`. Static calls are never resolved at runtime. – Andreas Mar 20 '16 at 02:46
  • 2
    I've also found the sentence `In this specification, whenever we speak of a class or interface type, we include the generic version as well, unless explicitly excluded.` (4.5) So it looks like you're right - it's allowed simply because it isn't disallowed. – Paul Boddington Mar 20 '16 at 02:48
  • @Andreas I preempted your response and already clarified what I meant by the last sentence. The fact of the matter still stands that if you take out all the misleading and irrelevant information from this answer, you're left with just the first line. – Obicere Mar 20 '16 at 02:48
  • @PaulBoddington once again, there is confusion between type variables and type parameters. Your question is about type variables. And that also refers merely to the rest of the document when they are giving example code to avoid confusion and has no impact on the actual JVM or the question. – Obicere Mar 20 '16 at 02:51
  • @Obicere I'll be honest I don't know exactly what is meant by "the generic version" in that quote. Is it just a type variable? – Paul Boddington Mar 20 '16 at 02:54
  • @PaulBoddington it just means that in specification they'll write `ArrayList` instead of `ArrayList` unless they are purposefully intending to use the raw type. – Obicere Mar 20 '16 at 02:56
  • 1
    @Obicere A `type parameter` *is* a `type variable`. [JLS 8.1.2](https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.2): *A class is generic if it declares one or more type variables. These type variables are known as the type parameters of the class*. Not to be confused with [Parameterized Types](https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.5). – Andreas Mar 20 '16 at 02:56
  • @Andreas just because `A -> B`, that doesn't mean that `B -> A`. Your answer discusses the limitations of a type parameter and by doing so goes off-topic. These are quite separate and the point still stands. If you hadn't included any mention of `8.4.4` or `8.1.2` I wouldn't have even blinked an eye. – Obicere Mar 20 '16 at 03:00
  • 2
    I've found this (15.12.1) `If the form is Primary . [TypeArguments] Identifier, then let T be the type of the Primary expression. The class or interface to search is T if T is a class or interface type, or the upper bound of T if T is a type variable.`. Seems it is explicitly discussed. – Paul Boddington Mar 20 '16 at 03:02
  • @PaulBoddington Doesn't cover the "good reason" part of your question, but definitely means that it was deliberately allowed. Good find. You should self-answer your question. – Andreas Mar 20 '16 at 03:05