Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unspecified type parameter when calling generic method

Tags:

java

generics

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...

like image 494
Alexey Y. Avatar asked Sep 14 '16 13:09

Alexey Y.


People also ask

What is type parameter generics?

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.

How do you call a generic method?

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.

How many type parameters can be used in a generic class?

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.

Is it necessary to use T as the name of a type parameter?

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.


2 Answers

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.

like image 114
Denis Rosca Avatar answered Sep 22 '22 10:09

Denis Rosca


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

like image 23
bajermi2 Avatar answered Sep 19 '22 10:09

bajermi2