Rishab Shinghal
Rishab Shinghal

Reputation: 591

How Automatic Type Inference works if the constructor parameter is not generic

Case 1:

class Gen3<T extends Number> {
    T val;

    <T extends String> Gen3(String ob){

    }
}

Here compiler doesn't give any error, but it should give right? Because here are two contradicting bounds for T. Please help me in understanding this.

Case 2:

class Gen3<T extends Number> {
    T val;

    <T extends String> Gen3(String ob) {

    }           
}

Suppose if I write following to test the above class

Gen<Integer> a = new Gen<>("r");

Now how automatic type inference would work here?

Please help in understanding this.

Upvotes: 2

Views: 127

Answers (3)

Ralf Kleberhoff
Ralf Kleberhoff

Reputation: 7290

This "use case" of Generics doesn't make sense in multiple aspects. With the <T extends String> clause you introduce a type variable that you don't use and don't give the compiler a chance to replace it with a concrete type in a given call situation.

Your definition is equivalent to the following (I just renamed the two different type variables to have different names, making the discussion easier):

class Gen3<T extends Number> {
    T val;
    <U extends String> Gen3(String ob) {

    }
}

The <U extends String> clause tells the compiler: "The following constructor will use a type parameter U, and I only allow U to be String or a subclass of String". As others already said, String is final, so U can only be String, so it isn't really a variable type, and declaring a variable type that can't vary doesn't make sense. I'll continue with a modified version:

class Gen3<T extends Number> {
    T val;
    <U extends Collection> Gen3(String ob) {

    }
}

If you do Gen<Integer> a=new Gen<Integer>("r");, how should the compiler find out the concrete class to replace U with? The <Integer> part applies to the T variable, so it doesn't help for U. As you don't refer to U in any of the arguments, there's no hint for the compiler.

The idea of Generics is that a class has some elements where you want to allow for varying types, and allow the compiler to flag misuse, e.g. add an Integer to a List<String>:

List<String> myList = new ArrayList<String>();
myList.add(new Integer(12345));

Here, the compiler can match the generic List<E> type parameter E to be a String (from the List<String> declaration). In this context, the gegeric List.add(E e) method declaration becomes an add(String e), and doesn't match the usage with new Integer(12345), which isn't a String, allowing the compiler to flag the error.

Summary:

Introduce a type parameter only if you give the compiler a chance to deduce it from the call arguments.

Upvotes: 1

Eran
Eran

Reputation: 393936

There are no two contradicting bounds of T. There are two type variables that happen to have the same name. In the constructor, the type parameter T hides the type parameter of the class level.

Note that the issue is not with the different type bounds. If you actually try to do something with the type parameter, such as:

class Gen3<T extends Number> {
    T val;
    <T extends Number> Gen3(T ob) {
        val = ob;
    }
}

This won't pass compilation even if both Ts have the same type bound, since the type parameter of ob is different than the type parameter of val.

Upvotes: 5

luk2302
luk2302

Reputation: 57164

"Because here are two contradicting bounds for T" - no. There simply a two separate definitions of T that have nothing to with each other. The T on the constructor hides the T of the class, same with local variables vs. fields of the same name. A "proper" IDE will tell you that the inner T hides the outer T and is unused.

Upvotes: 2

Related Questions