Unspecified type parameter when calling a generic method

Suppose we have the following general class:

public class GenericClass<U> { private U value; public GenericClass(U value) { this.value = value; } } 

and the following general method in another MyClass class:

 public <T extends BT, BT> void genericMethod(T arg) { Object genericClass = new GenericClass<BT>(arg); } 

What value will a parameter of type BT get if we call

 genericMethod("text"); 

?

Some notes:

The above code compiles without errors or warnings, which seems strange to me. Decompilation (using IntelliJ IDEA 2016) shows the following code:

 public <T extends BT, BT> void genericMethod(T arg) { new MyClass.GenericClass(arg); } 

Note that the new GenericClass<BT>(arg) does not match the new GenericClass(arg) , because the latter is equivalent to new GenericClass<T>(arg) (type inference), and although T extends BT , these are different types and GenericClass can have internal logic, where the exact type name plays an important role (for example, used as a string key on some map, etc.). Therefore, it is strange for me why the compiler silently uses type inference, instead of causing a warning (or, possibly, even an error), which indicates that a parameter of type BT is not specified. Maybe I'll miss smth. important about generics in Java, however ...

+6
source share
2 answers

The first part of this answer will cover the output part of the type of your question.

The only place where the type is automatically inferred in your example is to call genericMethod .

Definition genericMethod declares two typical types: an unbound BT and T which should be a subclass of BT .

Type T will be inferred in accordance with the rules specified in the Java Language Specification Section 18.2.4 :

The form constraint formula , where S and T are types, is reduced as follows:

...

Otherwise, if T is an output variable, α and S is not a primitive type, the restriction reduces to the estimate S = α.

In your example, you provide an object of type String as an argument of type T According to the above rule, T will be considered equal to String .

Now that T fixed, the output will continue using BT . This type is deduced as follows, section 18.3.1 , which states that an expression of type:

T extends BT, T == String

means

String extends BT

This relationship allows the compiler to bind BT to String , so you get the following implied call:

 genericMethod<String,String>("text") 

Hope this makes it a little easier.

To answer the second part of your question:

and although T extends BT, these are different types, and GenericClass may have internal logic where the exact type name plays an important role (for example, used as a string key on some map, etc.) .

Without additional restrictions, the compiler will treat T as the equivalent of BT and allow you to call only methods defined as part of the BT type.

Unless you are doing something very strange using reflection, the code should not depend on the value of the execution time of the type argument. The current GenericClass in its current form is not very useful, because U has no restrictions, so the compiler will allow you to do only those things that are valid for all objects. You can view U in this context just like Object .

+3
source

Based on this part of your question

GenericClass can have internal logic, where the exact type name plays an important role (for example, used as a string key on some map, etc.).

I think there might be some kind of confusion about how these types are derived. It is important to understand that the type that GenericClass will contain is the type of runtime you are passing through. Any dependency on the exact type that you could do internally (although usually it really shouldn't be) will work just fine.

In general, type checks performed at compile time may seem a little free (especially if you came from C ++, for example). The answers to this question have an interesting discussion. Java generics - type erasure - when and what happens

+1
source

All Articles