1

My understanding is that a generic Java class needs to be parameterized over types before it can be put to use. I am surprised that the following sample code, in which the generic class has not been parameterized, executes without any errors.

public class Box<T> {
  private T t;

  public static void main(String[] args) {
    System.out.println("It actually executed!!!!");
  }

  public void set(T t) { this.t = t; }

  public T get() { return t; }
}  

java Box
produces output
It actually executed!!!!

Is there an implict type that gets passed to the generic class in this case?

Sandeep
  • 1,245
  • 1
  • 13
  • 33
  • 2
    why would it not work? main is a method with no reference to T. It is not usually the case where you would have a main entry point for a generic class definition, so this example is a bit contrived. – OldProgrammer Oct 28 '19 at 16:59
  • 1
    There's no instance of your class, so nothing is ever assigned to the type parameter. – rgettman Oct 28 '19 at 16:59

4 Answers4

0

You are not "executing a class here". Rather you are executing the static main() method in the class. The reason you are allowed to do this is because of type erasure. Generic types are only enforced when you compile your code. Without declaring any references or creating instances of Box<T> the compiler has no reason to check the generic types. When you run the program, the interpreter knows nothing about the generic code due to type erasure, so it happily runs the program.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
0

Java is not like C. There isn't 'one variant of the Box class for every imaginable T / every T that is actually used in the code base'. There's just Box. For example:

new Box<String>().getClass() == new Box<Integer>().getClass() // this is true

You will not have a separate class loaded, etc.

This does have some side effects: It is IMPOSSIBLE to derive String from the variable x in: Box<?> x = new Box<String>(); - that information is now gone. This is called erasure.

Effectively, generics are almost entirely a figment of the compiler's imagination. It's like typing info in typescript and such: It is there at compile time and the compiler will use it to for example tell you that types don't line up (compiler errors), but once the compiler accepts it all, it gets compiled into a form such that, at runtime, this information is simply eliminated entirely*.

This is why the above code works without complaint: You have a static method, the <T> doesn't even exist here.

Let's zoom in on your get and set methods and the 't' field: They are exactly is if they read: private Object t; public void set(Object o) { this.t = t; } public Object get() { return t; } with only one exception: If, at compile time, it doesn't make sense, the compiler will refuse to compile it. Furthermore, for any callers to the get call, a cast is silently included.

*) Not quite entirely; any usage of generics in signatures, so, the types of fields, your class's own definition, and your implements and extends lines, and in the return types or parameter types of your methods, is not eliminated, but it is as if it is a comment to the VM: The VM does not care about this stuff; it exists solely so that, if you are invoking javac with some stuff on the classpath, that javac knows what the generics are when interacting with these members. That is all.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
0

Generic inside java was designed to provide compile-time type safety for your object. For your case, it is not accessing t variable at all inside main() method in the class. So java will happily compile and run the programs.

If you instantiate the Box class inside the main() method:

Box b = new Box(); // this is will produce "unsafe operations" note
b.set("this is the string");
System.out.println(b.get().getClass().getName());

based on the last variable assignment, you will get a string here.

chris
  • 89
  • 3
0

The method you're asking about is main(), which looks like below (I edited the class and method just to the bare minimum). As you've found, it works.

class Box<T> {
    public static void main(String[] args) {
        // do something
    }
}

That method is fine because it has no association with generic type T. Here, main is a static method, meaning it is called without an object instance.

Generics don't work for static methods (or fields), but your example above isn't doing that. Here's a different example that tries to define a static method that also uses T (but it doesn't work):

class Box<T> {
    public static void bar(T args) {
        // do something
    }
}

This example, being a static method, would be invoked without an object instance - like this: Box.bar(someArgs) - but that's invalid. Without first creating an instance of Box with some generic type, how would the compiler know what T is?

It is possible to define a static method to use a generic type, but one that is fully independent from T in your example. Here's a way to do that for some separate generic type X:

class Box<T> {
    public static <X> void foo(X input) {
        // do something
    }
}

and you could call it like this: Box.foo("");

Kaan
  • 5,434
  • 3
  • 19
  • 41