Say I have a generic class Foo
which can hold an object of type T
. Furthermore, let's say I only want to be able to instantiate the class with objects that are one of two types. Finally, let's say that the lowest common upper bound of these two types is a type that has many more subclasses than those two types that I want to allow, so I can't simply specify an upper bound for the type parameter (as in class Foo<T extends Something>
), because then I would allow to instantiate the class with other types than the two I expect.
For illustration, let's say I want Foo
to hold only either a String
or an Integer
. The lowest common upper bound is Object
, so specifying an upper bound won't do the trick.
Certainly, I could do something along the lines of
class Foo<T> {
private T obj;
public Foo(T obj) throws IllegalArgumentException {
if (!(obj instanceof String || obj instanceof Integer)) {
throw new IllegalArgumentException("...");
}
this.obj = obj;
}
}
However, in this case, I can still call the constructor with any object; if I try to instantiate it with something that is neither a String
nor an Integer
, I will get an exception at runtime.
I would like to do better. I would like the compiler to infer statically (i.e., at compile time) that I can only instantiate this class with objects that are either String
or Integer
.
I was thinking something along those lines might do the trick:
class Foo<T> {
private T obj;
public Foo(String s) {
this((T) s);
}
public Foo(Integer i) {
this((T) i);
}
private Foo(T obj) {
this.obj = obj;
}
}
This works, but it looks really, really odd. The compiler warns (understandably) about unchecked casts. Of course I could suppress those warnings, but I feel this is not the way to go. In addition, it looks like the compiler can't actually infer the type T
. I was surprised to find that, with the latter definition of class Foo
, I could do this, for instance:
Foo<Character> foo = new Foo<>("hello");
Of course, the type parameter should be String
here, not Character
. But the compiler lets me get away with the above assignment.
- Is there a way to achieve what I want, and if yes, how?
- Side question: why does the compiler let me get away with the assignment to an object of type
Foo<Character>
above without even so much as a warning (when using the latter definition of classFoo
)? :)