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

后端 未结 7 469
清歌不尽
清歌不尽 2021-01-11 19:17

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

相关标签:
7条回答
  • 2021-01-11 19:37

    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
    
    0 讨论(0)
  • 2021-01-11 19:37

    You can use so-called "witness" types to 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 reason your example doesn't make any sense is because you gain virtually nothing from using a generic in this context. What possible algorithm will reverse an integer or a string or a MagicValue without resorting to awful run-time type-checking and casting? The code will be all three reverse algorithms, wrapped with a hideous if-ladder.

    0 讨论(0)
  • 2021-01-11 19:38

    And if you only want one instance of the 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
    
    0 讨论(0)
  • 2021-01-11 19:39

    So here is the why (worked it out at work)

    Generics are always from a subclass, although it looks like

    Public class Thing<T> {}
    

    will allow any type in there, really what it's saying is that it will allow any subtype of Object. I.e.

    Public class Thing<T extends Object> {}
    

    This is effectively working as inheritance, and indeed, the Oracle Website shows us this happening when the syntactic sugar is removed:

    In the following example, the 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 Java compiler replaces the bounded type 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 so the answer turns out that the reason you can't limit the types in this way is because it effectively turns into multiple Inheritance, which is nasty, and which I'm happy to avoid....

    0 讨论(0)
  • 2021-01-11 19:40

    An alternative would be to just accept the fact that you have no control over the type hierarchy of String/Integer and create an interface to give a common type for the 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();
    }
    
    0 讨论(0)
  • 2021-01-11 19:51

    You can't bound a generic parameter to range of values. You could however restrict it 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);
    
    }
    
    0 讨论(0)
提交回复
热议问题