Static polymorphism with generics in Java

I was hoping to create some clean code that can recursively dig through collections and print the first integer it finds. Coming from a C ++ background, this approach looked like this:

class Test {
static void print(Integer i) {
  System.out.println(i);
}

static <T> void print(ArrayList<T> arr) {
  T entry= arr.at(0);
  Test.print (entry);
}

static void Test() {
    ArrayList<ArrayList<Integer>> arrayOfArrayOfInt= create();
    print( arrayOfArrayOfInt );
}
}

      

Unfortunately this doesn't work.

One alternative is to ditch static polymorphism and create a print (Object o) function, and then run a series of instanceof checks to transition into the correct behavior. Is this the only way to do it due to Java style erasure or a more elegant approach?

+3


source to share


5 answers


The next method will recursively dig in Object

and return Optional

containing the first Integer

one it finds, or Optional.empty()

if it cannot find one.

static Optional<Integer> firstInt(Object o) {
    if (o instanceof Integer)
        return Optional.of((Integer) o);
    if (o instanceof int[]) {
        int[] array = (int[]) o;
        return array.length > 0 ? Optional.of(array[0]) : Optional.empty();
    }
    if (o instanceof Object[])
        return firstInt(Arrays.asList((Object[]) o));
    if (o instanceof Iterable) {
        for (Object o2 : (Iterable<?>) o) {
            Optional<Integer> result = firstInt(o2);
            if (result.isPresent())
                return result;
        }
    }
    return Optional.empty();
}

      

You can do it using polymorphism, but you will need to create an interface Searchable

like this

interface Searchable {
    Optional<Integer> search();
}

      



and then create wrapper classes for all the concrete types you want to find. For example:

public final class SearchableList<T extends Searchable> extends AbstractList<T> implements Searchable {

    private final List<T> list;

    SearchableList(List<T> list) {
        this.list = list;
    }

    // rest omitted
}

      

It would be such a convoluted mess, however, and I would avoid it, preferring validation instead instanceof

.

+2


source


You are correct in thinking that you cannot consider java generics as you would use the C ++ template.

So, without instanceof

, this will not assume that the entry is an integer (neither at runtime nor at compile time):

static <T> void print(ArrayList<T> arr) {
  T entry= arr.get(0);
  Test.print (entry);
}

      



So, to "create clean code that can recursively dig into a collection and print the first integer it finds" (or rather, the first non-list element at index 0) in java:

static void print(Object obj) {
    System.out.println(obj);
}

static <T> void print(List<T> arr) {
    T entry = arr.get(0);
    if (entry instanceof List)
        print((List<?>) entry);
    else 
        print(entry);
}

static void test() {
    List<List<Integer>> arrayOfArrayOfInt = create();
    print(arrayOfArrayOfInt);
}

      

Not an elegant solution that you need, but a possible solution that a generic type system requires.

+2


source


Short answer: Yes, give up static polymorphism and use instanceof

. Or just write a local loop to get your job done.

Java generators are much weaker than C ++ templates. In particular, the compiler will not create new specializations for you. All you can do with them is to do more restrictive static type checking on classes and methods that are already defined with normal means (for example, you wrote them by hand or generated them with a tool). As you suspected, the type of erasure matters.

With Java generics, this code:

static <T> void print(ArrayList<T> arr) {
  T entry= arr.at(0);
  Test.print (entry);
}

      

always effectively addresses this because of the style erasure:

static Object void print(ArrayList<Object> arr) {
  Object entry= arr.at(0);
  Test.print (entry);
}

      

It will not create separate specializations as C ++ templates.

Your specific piece of code is not working because it lacks several specific methods that you will need to provide manually. But I think I can see where you are trying to go. (My guess is that you want code that works with a wider range of types than just plain List<List<Integer>>

.) And you won't get a satisfactory solution to that using Java generics. (You can get it to work, but you'll have to write a lot of code by hand, which templates won't help you. And there would be some random edges). Go to dynamic type checker and instanceof

; what's the Java way to do it.

+1


source


First of all, the function to get the "get" element is not the "at". However, the main problem is that you are trying to call the "print" function, which takes an integer, but passes it a value of type "T."

If you are going to print print and pass a value of unknown type to it, the function must either take an argument of type Object or take a general argument, for example

public <T> void print(T item)

      

or

public void print (Object item)

      

Java will only respond to function calls for the most specific overload at compile time, if you are trying to find the correct overload at runtime you will have to use instanceof. Alternatively, you can create a HashMap that points class objects to Consumer objects.

HashMap<Class<?>, Consumer<?>> prints = new HashMap<>();
void setup() {
    prints.put(ArrayList.class, (list) -> {
        for (Object o : list) {
            print(o);
        }
    }

    prints.put(Integer.class, (integer) -> System.out.println(integer));
}

void print(Object o) {
    Consumer<?> func = prints.get(o.getClass());
    func.accept(o);
}

      

+1


source


If you want to keep this code as it is, you will have to add another print method for objects.

public static void print(Object o){}

      

But there are more appropriate approaches in your example.

0


source







All Articles