Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I force two instance variables to be of the same generic type?

Tags:

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     } } 
like image 883
vvye Avatar asked Jun 15 '18 12:06

vvye


People also ask

How do I get a class instance of generic type T?

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.

Can generic class instance be created?

Java is type-safe, meaning that only Objects are able to create instances.

How do you make an object of a generic class?

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

Can method and instance variable have same name?

Yes it's fine, mainly because, syntactically , they're used differently.


1 Answers

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)?

like image 127
Lino Avatar answered Nov 04 '22 08:11

Lino