使 Java 类通用,但仅适用于两种或三种类型

问题描述 投票:0回答:8

(我很惊讶在 stackoverflow 上找不到这个问题,我只能将其归咎于我的谷歌搜索不佳,无论如何都要指出重复的问题......)

这是一个玩具类,它返回与您放入其中的内容相反的内容。目前它适用于整数,但只需要非常小的更改即可适用于字符串。

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

}

我想做的是使类通用,但仅适用于两种或三种可能的类型。所以我想写的是:

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

X value

public X get(){
[...]

正确的语法是什么?我的 Google-fu 让我失望了……:(

编辑:它看起来不是正确的语法,而且似乎无法完成,所以我的主要问题是:为什么?这似乎是人们在将课程真正通用之前可能想做的事情……

编辑编辑:今天设法与一些实验室伙伴一起找出原因,因此在下面添加了相关的原因答案。

java generics
8个回答
16
投票

不幸的是java并没有直接提供这样的功能。不过,我可以建议您采取以下解决方法:

使用私有构造函数和 3 个静态工厂方法创建参数化类

Mirror
,这些方法使用特定参数创建
Mirror
的实例:

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

编辑 显然,您可以(并且可能应该)将类

Mirror
与其创建分开,例如将工厂方法放在单独的类中
MirrorFactory
。在这种情况下,构造函数应该受到包保护。

如果您想支持大量但数量有限的类,您可以仅实现一种通用工厂方法

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

方法

checkTypeSupported(value);
可能会使用某种元数据(例如属性、JSON等文件)来获取支持的类型。然而在这种情况下,您将无法享受编译时验证。

其他解决方案是要求所有受支持的类型扩展某些基类或实现接口:

public class Mirror<T extends MyInterface> {}

但是这个解决方案似乎不符合您的要求,因为您需要

Integer
String
MagicValue


3
投票

有多种方法可以满足您的需求...这是另一种选择。没有 getter 或 setter。
每种要处理的类型都有一个 Mirror 实例。一种reverse()方法。 根据需要进行调整。添加错误检查/处理。

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

测试员:

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

结果:

Do for String
Do for Integer
Do for Integer
Do for Integer
Do for JFrame

2
投票

另一种选择是接受您无法控制 String/Integer 的类型层次结构的事实,并创建一个接口来为您可以控制的类提供通用类型

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

2
投票

如果您只想要 Mirror 类的一个实例...请使用通用方法。

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

测试员:

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

结果:

Do for String
Do for Integer
Do for Integer
Do for Integer
Do for JFrame

1
投票

您无法将通用参数绑定到值范围。但是,您可以通过编程方式限制它:

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

}

1
投票

您可以使用所谓的“见证”类型来使编译器执行您想要的操作。

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 > {
    // ...
}

但是,您的示例没有任何意义的原因是因为在这种情况下使用泛型几乎没有任何好处。有什么算法可以反转整数、字符串或 MagicValue,而不需要采用糟糕的运行时类型检查和转换?该代码将是所有三种反向算法,并用一个可怕的

if
梯子包裹起来。


0
投票

所以这就是为什么(在工作中解决了)

泛型总是来自子类,尽管它看起来像

Public class Thing<T> {}

将允许其中存在任何类型,实际上它的意思是它将允许对象的任何子类型。 IE。

Public class Thing<T extends Object> {}

这实际上是作为继承起作用的,事实上,Oracle 网站向我们展示了当语法糖被删除时会发生这种情况:

在以下示例中,通用 Node 类使用有界类型 参数:

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; }
    // ...
}

Java 编译器将有界类型参数 T 替换为第一个 绑定类,可比较:

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; }
    // ...
}

...所以答案是,你不能以这种方式限制类型的原因是因为它实际上变成了多重继承,这是令人讨厌的,我很乐意避免......


0
投票

我来到这里是为了搜索如何创建具有三个类型参数的泛型类。最后,我得出了三个论点,其中一个论点类似于下面的代码:

示例,

公开课机器人{

private T t;
private S s;
private V v;

public void add(T t, S s, V v) {
      this.t = t;
      this.s = s;
      this.v = v;
    }

}

公共类 MyRobot 扩展了 Robot { }

哈哈!

© www.soinside.com 2019 - 2024. All rights reserved.