Reputation: 591
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
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
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 T
s have the same type bound, since the type parameter of ob
is different than the type parameter of val
.
Upvotes: 5
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