Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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");
like image 352
Cel Avatar asked Nov 30 '11 17:11

Cel


People also ask

What is the generic T?

Generic Class T is called type parameter, which can be used as a type of fields, properties, method parameters, return types, and delegates in the DataStore class. For example, Data is generic property because we have used a type parameter T as its type instead of the specific data type.

Why do we use T in generics?

In other words, the T is an actual part of the syntax for Generics and it means that the paramter for the Class can be of variable type? <T> is the generic type. Maybe read the official tutorial. Yes, the angle-brackets with one (or more) types is the syntax for generics.

What is <? Super T in Java?

super T denotes an unknown type that is a supertype of T (or T itself; remember that the supertype relation is reflexive). It is the dual of the bounded wildcards we've been using, where we use ? extends T to denote an unknown type that is a subtype of T .

Can a generic class be extended?

You can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or interface and the type parameters of another are determined by the extends and implements clauses.


3 Answers

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?

like image 153
Guillaume Avatar answered Oct 05 '22 21:10

Guillaume


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));
like image 21
Fil Avatar answered Oct 05 '22 19:10

Fil


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.

like image 29
Matthias Ronge Avatar answered Oct 05 '22 21:10

Matthias Ronge