[ACCEPTED]-Make a Java class generic, but only for two or three types-generics

Accepted answer
Score: 14

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.

Score: 3

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
Score: 2

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();
}
Score: 2

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
Score: 1

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

}
Score: 1

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.

Score: 0

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