Assuming I have a class with two instance variables of generic type, how can I force those types to be the same?
I haven't found any similar questions elsewhere (let alone answers), which may be because I'm using terms wrong. There's this question, but I'm not sure how to apply it to my situation.
In short, how can I get this last line of code to not compile?
class Foo<T> { private T value; } class Bar { private Foo var1; private Foo var2; public Bar(Foo var1, Foo var2) { this.var1 = var1; this.var2 = var2; } public static void main(String[] args) { Foo<String> var1 = new Foo<>(); Foo<Integer> var2 = new Foo<>(); Bar b = new Bar(var1, var2); // this should not work } }
The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. A solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.
Java is type-safe, meaning that only Objects are able to create instances.
Like C++, we use <> to specify parameter types in generic class creation. To create objects of a generic class, we use the following syntax. Note: In Parameter type we can not use primitives like 'int','char' or 'double'.
Yes it's fine, mainly because, syntactically , they're used differently.
Make Bar
also generic:
class Bar<T> { private Foo<T> var1; private Foo<T> var2; public Bar(Foo<T> var1, Foo<T> var2) { this.var1 = var1; this.var2 = var2; } public static void main(String[] args) { Foo<String> var1 = new Foo<>(); Foo<Integer> var2 = new Foo<>(); Bar<String> b = new Bar<>(var1, var2); // this does not work } }
By using a class level generic parameter T
for Bar
you can enforce the same types for both instance variables.
This eliminates also the warning of using raw types (which should never be used) as mentioned by @daniu in the comments.
Now if you happen to not use raw types but want to allow not the same types, you can work with wildcards:
With upper bounded, which only allow typed reading (var1
and var2
will always produce an implementation of type T
):
class Bar<T> { private Foo<? extends T> var1; private Foo<? extends T> var2; public Bar(Foo<? extends T> var1, Foo<? extends T> var2) { this.var1 = var1; this.var2 = var2; } public static void main(String[] args) { Foo<String> var1 = new Foo<>(); Foo<Integer> var2 = new Foo<>(); Bar<Object> b = new Bar<>(var1, var2); // this does now work } }
And with lower bounded, which only allow typed writing (var1
and var2
will always consume any implementation of type T
):
class Bar<T> { private Foo<? super T> var1; private Foo<? super T> var2; public Bar(Foo<? super T> var1, Foo<? super T> var2) { this.var1 = var1; this.var2 = var2; } public static void main(String[] args) { Foo<Integer> var1 = new Foo<>(); Foo<Number> var2 = new Foo<>(); Bar<Integer> b = new Bar<>(var1, var2); // this does now work } }
For more on that topic you can read: What is PECS (Producer Extends Consumer Super)?
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