Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make a Java class generic, but only for two or three types

Tags:

java

generics

(I was astonished not to be able to find this question already on stackoverflow, which I can only put down to poor googling on my part, by all means point out the duplicate...)

Here is a toy class that returns the reverse of what you put into it. Currently it works on integers, but would require only very minor changes to work for String.

public class Mirror {

  int value;

  public int get() {
    return reverse(value);
  }

  private int reverse(int value2) {
    String valueString = value + "";
    String newString = reverse(valueString);
    return Integer.parseInt(newString);
  }

  private String reverse(String valueString) {
    String newString = "";
    for (char c : valueString.toCharArray()) {
      newString = c + newString;
    }
    return newString;
  }

  public void set(int value) {
    this.value = value;
  }

}

What I'd like to do is make the class generic, but only for, say, two or three possible types. So what I want to write is:

public class Mirror<X, where X is one of Integer, String, or MagicValue {

X value

public X get(){
[...]

What's the correct syntax? My Google-fu is failing me... :(

EDIT: it appears there isn't a correct syntax and it can't appear to be done, so my main question is: why? this seems like the sort of thing that people might want to do before they made the class truly generic...

EDIT EDIT: Managed to work out the why with some labmates today, so added the relevant why answer below.

like image 450
Joe Avatar asked May 18 '14 12:05

Joe


2 Answers

Unfortunately java does not provide such functionality directly. However I can suggest you the following work around:

Create parametrized class Mirror with private constructor and 3 static factory methods that create instance of 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 can (and probably should) separate the class Mirror from its creating, e.g. put the factory methods to separate class MirrorFactory. In this case the constructor should become package protected.

If you want to support large yet limited number of classes you can implement only one generic factory method

    public static <T> Mirror<T> createMirror(T value) {
        checkTypeSupported(value);
        return new Mirror(value);
    } 

Method checkTypeSupported(value); may use some kind of metadatat (e.g. properties, JSON etc file) to get supported types. In this case however you will not enjoy the compile time validation.

Other solution is to require that all supported types extend certain base class or implement interface:

public class Mirror<T extends MyInterface> {}

But this solution seems does not match your requirements since you need Integer, String and MagicValue.

like image 178
AlexR Avatar answered Nov 07 '22 21:11

AlexR


Various ways to do what you need...Here is another option. No getter or setter.
One instance of Mirror for each type to be handled. One reverse() method. Tweak as necessary. Add 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
like image 32
Java42 Avatar answered Nov 07 '22 21:11

Java42