简体   繁体   中英

How do I restrict accepting only one type in my generic method?

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.

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 eg 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.

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

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

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

You can take advantage of the fact that foo returns the same type <T> as the input parameters.

You can infer <T> by defining the return type:

Integer i1 = 4;
String s = "string";

final Integer i2 = foo(i1, s); // error, only Integer allowed

If you do not specify a return type, the type <T> will be inferred as Object , and thus all sub-classes will be accepted.

Alternatively, as @Finbarr mentions, you can infer the type via

Foo.<Integer>foo(i1, s); // error, only Integer allowed

To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound.

Following method will only accept numbers as it's parameters.

public static <T extends Comparable<T>> T maximum(T firstNumber, T secondNumber)
{                      
   system.out.println(secondNumber.compareTo(firstNumber));
}

If you don't extend it with Comparable then compareTo() will not be available.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM