使用泛型中的 OR 代替 AND <T extends Number | CharSequence>

31

有没有可能泛型地参数化一个接受ClassA或InterfaceB的方法?

由于| 伪代码而无法编译

public <T extends Number | CharSequence> void orDoer(T someData){ // ... }

比如说,我希望这个方法只接受一个Number或CharSequence类型的参数,而不是多个不同类型的参数。

此方法应该可以接受Number或CharSequence类型的参数

orDoer(new Integer(6));
int somePrimitive = 4;
orDoer(somePrimitive);
orDoer("a string of chars");

4
如果有两个分别接受NumberCharSequence参数并委托给第三个私有方法来执行工作的方法,那么在这个方法内部你会做些什么,而这些事情不能通过这两个独立的方法来完成? - Philipp Reichart
1
如果我只处理两种类型,那么我同意收益微不足道,但是这似乎是一种更简洁的委托更多参数类型(它们没有共用的可用超类型)的方式。 - Cel
1
你应该看看Ceylon的联合类型。 ;) - Anonsage
3个回答

20

如果你真的想这样做,你需要在自己的自定义类中包装你接受的类。在你的例子中,可能是这样的:

public class OrDoerElement {
    private final Number numberValue;
    private final CharSequence charSequenceValue;

    private OrDoerElement(Number number, CharSequence charSequence) {
        this.numberValue = number;
        this.charSequenceValue = charSequence;
    }

    public static OrDoerElement fromCharSequence(CharSequence value) {
        return new OrDoerElement(null, value);
    }

    public static OrDoerElement fromNumber(Number value) {
        return new OrDoerElement(value, null);
    }
}

你的orDoer方法变成了:

public void orDoer(OrDoerElement someData) { .... }

然后你可以构建其中之一,并在你的方法中使用,使用以下任意一种方式:

orDoer(OrDoerElement.fromCharSequence("a string of chars"));
orDoer(OrDoerElement.fromNumber(new Integer(6)));

但说实话,这听起来有点太复杂,而且为了能够使用不同参数类型调用方法而付出的工作太多了。您确定不能使用两种方法和第三种方法来实现相同的功能逻辑吗?


谢谢您的回答!我现在意识到我没有正确解释我的用例 - 我正在寻找与 T extends ClassA | InterfaceB 非常相似的东西,因为我希望我的方法的调用者能够直接传递他们的类型(例如 Integer 或 String)。也就是说,提供一个 store(T data) 方法,它将接受所有具有完全可重构特性的类型,这些类型可以从 .toString() 和标准单参数实例化(Number、CharSequence、Date 和许多其他可持久化类型)中进行重构。 - Cel
另一种我可以问的问题是为什么没有一个共享类型 - 在Object之前 - 用于Number,CharSequence和其他本地类型,这将使它们成为那些Uniconstructables或者你想称呼它们的任何东西的特征..(希望有意义) - Cel
是的,我明白,在某些情况下,这种超级超级类确实会证明其有用。 - Guillaume

1

对于原始问题:

public void orDoer(Object someData){
    assert someData instanceof Number || someData instanceof CharSequence;

    // ...
}

在你更具体的情况下,assert语句应该只使用内省来澄清对象是否具有所需的细节,即检查是否存在从字符串构造函数,尝试从传入对象的toString()结果创建对象的新实例,并比较两者是否相等:
public void orDoer(Object someData) {
    assert isUniconstructable(someData);
}

public static boolean isUniconstructable(Object object) {
    try {
        return object.equals(object.getClass().getConstructor(String.class)
            .newInstance(object.toString()));
    } catch (InstantiationException | IllegalAccessException | InvocationTargetException
            | NoSuchMethodException| RuntimeException e) {
        return false;
    }
}

由于可能会抛出异常,我们需要将assert测试封装到自己的函数中。
请注意,由于Android的ProGuard代码压缩会重写类名,因此内省可能会出现问题。在数据库中,不是YourClass而是类似于Q这样的类存储。当您想要使用后续版本的应用程序恢复它时,该类Q就是另一种形式了。有关此信息,请参见ProGuard网站。我只是想提醒您,在Android上使用内省时应该注意这一点。

1

你是否考虑使用匿名抽象类?当我需要类型安全的参数或返回类型时,我会使用下面代码的某个变体。话虽如此,我同意这里其他评论所说的,并且很好奇当你在为一组没有太多共同点的对象强制执行类型安全时,你真正获得了什么好处。

public abstract class Doer<T> {

  public void do(T obj) {
    // do some stuff. 
  }

}

// calling method

new Doer<Number>(){}.do(new Integer(5));

1
谢谢,请查看Guillaume帖子下的澄清。 - Cel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接