Imagine we have the following generic class:
public class GenericClass<U> {
private U value;
public GenericClass(U value) {
this.value = value;
}
}
and the following generic method in some other class MyClass:
public <T extends BT, BT> void genericMethod(T arg) {
Object genericClass = new GenericClass<BT>(arg);
}
Which value will BT type parameter obtain if we call
genericMethod("text");
?
Some notes:
The above code compiles without errors or warnings, which seems strange for me. Decompiling (by means of IntelliJ IDEA 2016) shows the following code:
public <T extends BT, BT> void genericMethod(T arg) {
new MyClass.GenericClass(arg);
}
Notice that new GenericClass<BT>(arg)
is not the same as new GenericClass(arg)
because the latter is equivalent of new GenericClass<T>(arg)
(type deduction), and although T extends BT
, these are different types, and GenericClass
may have internal logic where the exact type name plays an important role (e.g. is used as a string key in some map etc). So for me it is strange why the compiler silently uses type deduction instead of producing some warning (or maybe even error) stating that BT type parameter is not specified. Maybe I'm missing smth. important about generics in Java, however...
A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.
To call a generic method, you need to provide types that will be used during the method invocation. Those types can be passed as an instance of NType objects initialized with particular . NET types.
You can also use more than one type parameter in generics in Java, you just need to pass specify another type parameter in the angle brackets separated by comma.
Yes, it is mandatory to specify it. If you remove it and its usage in generic classes and methods they will become normal classes and methods.
The first part of this answer will address the type inference part of your question
The only place where a type is automatically inferred in your example is when calling the genericMethod
.
The definition of genericMethod
declares two generic type parameters: an unbound BT
and T which should be a subclass of BT
.
Type T
will be inferred according to the rules specified in Java language specification section 18.2.4:
A constraint formula of the form ‹S = T›, where S and T are types, is reduced as follows:
...
Otherwise, if T is an inference variable, α, and S is not a primitive type, the constraint reduces to the bound S = α.
In your example you provide an object of type String as an argument of type T
.
According to the rule above, T
will be inferred to be equal to String
.
Now that T
is fixed, the inference will proceed with BT
. This type is inferred by following section 18.3.1 which says that an expression of type:
T extends BT, T == String
implies
String extends BT
This relationship allows the compiler to bind BT
to String
, thus you end up with the following implied call:
genericMethod<String,String>("text")
Hope this clears things up a bit.
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 (e.g. is used as a string key in some map etc).
Without adding more constraints, the compiler will treat T
as equivalent to BT
and will allow you to call only the methods defined as part of the BT
type.
Unless you're doing something very weird using reflection, the code should not depend on the runtime value of type argument.
The current GenericClass
as it stands isn't very useful because U
doesn't have any constraints so the compiler will allow you to do only things that are valid for all objects.
You can consider U
in that context the same as Object
.
Based on this part of your question
GenericClass may have internal logic where the exact type name plays an important role (e.g. is used as a string key in some map etc).
I think there may be some confusion, about how these types are inferred. It is important to realize, that the type that GenericClass will contain is the runtime type of whatever you pass in. Anything dependent on the exact type, that you might be doing inside (although generally you really shouldn't be) will work just fine.
Overall the type checks done at compile time may seem bit loose to you (especially if you come from C++ for example). There is interesting discussion about some of it in the answers to this question. Java generics - type erasure - when and what happens
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With