[ACCEPTED]-Make a Java class generic, but only for two or three types-generics
Unfortunately java does not provide such 20 functionality directly. However I can suggest 19 you the following work around:
Create parametrized 18 class Mirror
with private constructor and 3 static 17 factory methods that create instance of 16 Mirror
with specific parameter:
public class Mirror<T> {
private T value
private Mirror(T value) {
this.value = value;
}
public static Mirror<Integer> integerMirror(Integer value) {
return new Mirror(value);
}
public static Mirror<String> stringMirror(String value) {
return new Mirror(value);
}
public static Mirror<MagicValue> magicMirror(MagicValue value) {
return new Mirror(value);
}
}
EDIT
Obviously you 15 can (and probably should) separate the class 14 Mirror
from its creating, e.g. put the factory 13 methods to separate class MirrorFactory
. In this case 12 the constructor should become package protected.
If 11 you want to support large yet limited number 10 of classes you can implement only one generic 9 factory method
public static <T> Mirror<T> createMirror(T value) {
checkTypeSupported(value);
return new Mirror(value);
}
Method checkTypeSupported(value);
may use some kind 8 of metadatat (e.g. properties, JSON etc 7 file) to get supported types. In this case 6 however you will not enjoy the compile time 5 validation.
Other solution is to require 4 that all supported types extend certain 3 base class or implement interface:
public class Mirror<T extends MyInterface> {}
But this 2 solution seems does not match your requirements 1 since you need Integer
, String
and MagicValue
.
Various ways to do what you need...Here 4 is another option. No getter or setter.
One 3 instance of Mirror for each type to be handled. One 2 reverse() method.
Tweak as necessary. Add 1 error checking/handling.
public class Mirror<T> {
public T reverse(final T value) {
T result = null;
while (true) {
if (value instanceof String) {
System.out.println("Do for String");
result = value;
break;
}
if (value instanceof Integer) {
System.out.println("Do for Integer");
result = value;
break;
}
if (value instanceof JFrame) {
System.out.println("Do for JFrame");
result = value;
break;
}
throw new RuntimeException("ProgramCheck: Missing handler for type " + value.getClass().getSimpleName());
}
return result;
}
Tester:
final Mirror<String> testerString = new Mirror<>();
testerString.reverse("string");
final Mirror<Integer> testerInteger = new Mirror<>();
testerInteger.reverse(41);
testerInteger.reverse(42);
testerInteger.reverse(43);
final Mirror<JFrame> testerJFrame = new Mirror<>();
testerJFrame.reverse(new JFrame());
Results:
Do for String
Do for Integer
Do for Integer
Do for Integer
Do for JFrame
An alternative would be to just accept the 4 fact that you have no control over the type 3 hierarchy of String/Integer and create an 2 interface to give a common type for the 1 classes you do have control over
public int reverse(int value) {
return Integer.valueOf(new StringBuilder(value + "").reverse()
.toString());
}
public String reverse(String value) {
return new StringBuilder(value + "").reverse().toString();
}
public <T extends Reversible> T reverse(T value) {
value.reverse();
return value;
}
public interface Reversible {
public void reverse();
}
And if you only want one instance of the 1 Mirror class...use a generic method.
public class Mirror {
public <T> T reverse(final T value) {
T result = null;
while (true) {
if (value instanceof String) {
System.out.println("Do for String");
result = value;
break;
}
if (value instanceof Integer) {
System.out.println("Do for Integer");
result = value;
break;
}
if (value instanceof JFrame) {
System.out.println("Do for JFrame");
result = value;
break;
}
throw new RuntimeException("ProgramCheck: Missing handler for type " + value.getClass().getSimpleName());
}
return result;
}
tester:
final Mirror tester = new Mirror();
String s = tester.reverse("string");
Integer i41 = tester.reverse(41);
Integer i42 = tester.reverse(42);
Integer i43 = tester.reverse(43);
JFrame j = tester.reverse(new JFrame());
results:
Do for String
Do for Integer
Do for Integer
Do for Integer
Do for JFrame
You can't bound a generic parameter to range 2 of values. You could however restrict it 1 programatically:
public abstract class AbstractMirror<T> {
T value;
protected AbstractMirror(Class<T> clazz) {
if (clazz != Integer.class && clazz != String.class && clazz != MagicValue.class)
throw new IllegalArgumentException();
}
public abstract T get();
protected abstract T reverse(T value);
}
You can use so-called "witness" types to 9 make the compiler do what you want.
public interface Reversible< T > {
public static final class IntReversible implements Reversible< Integer > {}
public static final class StringReversible implements Reversible< String > {}
public static final class MagicReversible implements Reversible< MagicValue > {}
}
public abstract class Mirror< T, R extends Reversible< T > > {
// ...
}
public class IntMirror extends Mirror< Integer, IntReversible > {
// ...
}
However, the 8 reason your example doesn't make any sense 7 is because you gain virtually nothing from 6 using a generic in this context. What possible 5 algorithm will reverse an integer or a string 4 or a MagicValue without resorting to awful 3 run-time type-checking and casting? The 2 code will be all three reverse algorithms, wrapped 1 with a hideous if
-ladder.
So here is the why (worked it out at work)
Generics 15 are always from a subclass, although it 14 looks like
Public class Thing<T> {}
will allow any type in there, really 13 what it's saying is that it will allow any 12 subtype of Object. I.e.
Public class Thing<T extends Object> {}
This is effectively 11 working as inheritance, and indeed, the Oracle Website shows 10 us this happening when the syntactic sugar 9 is removed:
In the following example, the 8 generic Node class uses a bounded type parameter:
public class Node<T extends Comparable<T>> { private T data; private Node<T> next; public Node(T data, Node<T> next) { this.data = data; this.next = next; } public T getData() { return data; } // ... }
The 7 Java compiler replaces the bounded type 6 parameter T with the first bound class, Comparable:
public class Node { private Comparable data; private Node next; public Node(Comparable data, Node next) { this.data = data; this.next = next; } public Comparable getData() { return data; } // ... }
...and 5 so the answer turns out that the reason 4 you can't limit the types in this way is 3 because it effectively turns into multiple 2 Inheritance, which is nasty, and which I'm 1 happy to avoid....
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.