views:

84

answers:

5

I have a generic function foo, which accepts any type and prints them out.

public static <T> T foo(T... arg) {
    List<T> foo = Arrays.asList(arg);
    for (T t : foo) {
      System.out.println(t);
    }
    return null;
  }

How do I make sure that the arguments received are of only 1 type. For example, {1,'a',3} should be invalid. It should either be all numbers or all characters. I want to accept either ALL integers or ALL Characters.

+1  A: 

The T part means that all the args will be the same type.

If you wanted to restrict your generic type to be only a certain type or sub-type (e.g. Integer) you can do the following:-

public static <T extends Integer> T foo(T... arg) {
    List<T> foo = Arrays.asList(arg);
    for (T t : foo) {
      System.out.println(t);
    }
    return null;
  }
Strawberry
You can't just write that way. That will mean, he needs two different methods - one for numbers and one for characters.
hgulyan
I want to accept either ALL integers or ALL Characters.
kunjaan
Can you, please, bring an example in your question? How do you figure out which type to you need to accept?
hgulyan
A: 

I'm not a java developer but what you can do as one possible option is use generic collection of objects of type T.

public static <T> T foo(List<T> arg) { 
    List<T> foo = arg; 
    for (T t : foo) { 
      System.out.println(t); 
    } 
    return null; 
  } 
this. __curious_geek
+2  A: 

You can in fact do something like this:

static <T extends Comparable<T>> void f(T... args) {
    System.out.println(java.util.Arrays.toString(args));
}
public static void main(String[] args) {
    // all one type -- all of these compile!
    f(1, 2, 3); // prints "[1, 2, 3]"
    f('a', 'b', 'c'); // prints "[a, b, c]"
    f("a", "b", "c"); // prints "[a, b, c]"
    f(1D, 2D, 3D); // prints "[1.0, 2.0, 3.0]"

    // this is not preventable
    f(1, (int)'a', 3); // prints "[1, 97, 3]"

    // mixture of types -- none of these compile!
    //f(1, 'a', 3); // compilation error!
    //f(1, '2', "3"); // compilation error!
    //f("a", "b", 'c'); // compilation error!
    //f(1, 2, 3D); // compilation error!
}

This takes advantage of the fact that:

So to match those types (and possibly others), we bound T as follows:

This does include things e.g. java.util.Date, which implements Comparable<Date>, and countless many other types, but is probably the best that you can do if you also want to allow Integer and Character.


Nonetheless, do keep in mind that Integer, Character, String, are all Object, so in fact a bunch of those mixed together IS a list of one type: Object.

Thankfully, it's NOT the case that Object implements Comparable<Object>; otherwise the above solution wouldn't work.

polygenelubricants
Clever. BTW, do you live here?
spong
A: 

You can't do exactly what you want to do. What you can do is overload the method for each type, and then delegate to a private method that handles the printing more generically:

public static <T extends Integer> void foo(T ... arg) {
    print(arg);
}

public static <T extends Character> void foo(T ... arg) {
    print(arg);
}

private static <T> void print(T ... arg) {
    for (T t : arg) System.out.println(t);
}

From an outside caller's point of view, the functionality is what you want:

foo(1, 2, 3);
foo('a', 'b', 'c');
foo(1, 'b', 3); // won't compile
SingleShot
No point even using generics for the two methods. Anything that extends `Integer` or `Character` will be autoboxed - might aswell just be `foo(Character ... arg)` and `foo(Integer ... arg)`.
Finbarr
Ha. True. Thanks :-)
SingleShot
A: 

You can do what you want to do like this:

YourClass.<Type>foo(params);

Specifically:

YourClass.<Integer>foo(1, 2, 3);

and

YourClass.<Character>foo('a', 'b', 'c');
Finbarr