Generic OR instead of AND <T extends Number | CharSequence>

浪子不回头ぞ 提交于 2019-12-17 16:32:12

问题


Is it possible to generically parameterize a method accepting EITHER ClassA OR InterfaceB ?

Does Not Compile Due to | Pseudocode

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

i.e. instead of writing multiple method signatures, I would like this one method to accept either a Number or CharSequence as an argument

Should Pass with a Number OR CharSequence argument

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

回答1:


If you really want to do that, you'll need to wrap youur accepted classes inside a custom class of your own. In your example case, probably something like:

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

And your orDoer method becomes:

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

Then you can build one of those and use in your method using either:

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

But honestly, that sounds a bit too complex and too much work just to be able to call a method with different parameter types. Are you sure you can't achieve the same using two methods, and a third method for the common logic?




回答2:


Is using an anonymous abstract class an option for you? When I need type safe parameters or return types, I use some variant of the code below. That being said, I agree with the other comments here, and am curious what benefit you really derive when you're enforcing a type safety for a group of objects that don't have all that much in common.

public abstract class Doer<T> {

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

}

// calling method

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



回答3:


For the original question:

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

    // ...
}

In your more specific case, the assert statement should just use introspection to clarify if the object has the specifics you want, i.e. check for a constructor from String, probe to create a new instance of the object from the toString() result of the incoming object, and compare both for equality:

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

(Because of the exceptions that may be thrown, we need to wrap the assert test into its own function.)

Be aware that introspection may break due to Android’s ProGuard code compression which rewrites the class names, and instead of YourClass just a Class, i.e. Q, is stored in the database, and when you want to restore it with a later version of your app which has more classes, class Q is something different then. See the ProGuard website for more information on this; I just wanted to notify that you should be aware of this when using introspection on Android.



来源:https://stackoverflow.com/questions/8330370/generic-or-instead-of-and-t-extends-number-charsequence

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!