Search code examples
javac++templatesmultidimensional-arraygeneric-programming

In Java, is writing a single function or class to handle arbitrary multidimensional ArrayList possible?


In C++, I can write 2 template classes, like:

#include <stdio.h>
#include <vector>
template <typename T>
class Printer{
public:
    static void print(T& v){
        printf("%d ",v.getValue());
    }
};

template <typename T,typename Alloc>
class Printer<std::vector<T,Alloc> >{
public:
    static void print(std::vector<T,Alloc>& v){
        for(T& e : v){
            Printer<T>::print(e);
        }
        printf("\n");
    }
};

so that it can accept arbitrary custom object type and vector in vector in vector... (infinity nested vector):

class Student{
public:
    int value;
    Student(int value){ this->value=value; }
    int getValue() const { return this->value; }
};

class Teacher{
public:
    int value;
    Teacher(int value){ this->value=value; }
    int getValue() const { return this->value*10; }
};

int main(){
    std::vector<Student> v2 = {Student(10),Student(20)};
    Printer<std::vector<Student> >::print(v2);

    std::vector<std::vector<Teacher> > v3 = {{Teacher(30),Teacher(40)},{Teacher(50),Teacher(60),Teacher(70)}};
    Printer<std::vector<std::vector<Teacher> > >::print(v3);
    return 0;
}

provided the custom object Student and Teacher has getValue() function.

But in Java, how can I write something like above that accepts both

ArrayList<Student > v2;

and

ArrayList<ArrayList<Teacher> > v3;

(and also ArrayList in ArrayList in ArrayList in...) in generic way?

Few problems occurs when I tried to convert the code, first, the generic object method cannot compile:

public static void <T> print(T t){
    System.out.print(t.getValue());
}

and I don't know how to convert std::vector< T , Alloc > in to Java because ArrayList only accepts 1 template parameter.

Note: I want vector to be the specialized template because when I want to add support of int:

std::vector<int> v1={1,2};
Printer<std::vector<int> >::print(v1);

I can just add

template <>
class Printer<int>{
public:
    static void print(int& v){
        printf("%d ",v);
    }
};

to the code.


Solution

  • You can do it with instanceof checks:

    public static <T> void print(List<? extends T> list) {
        for (final T t : list) {
            if (t instanceof List) print((List) t);
            else System.out.println(t);
        }
    }
    

    But of course you can not use a method T.getValue() if it is not defined anywhere in an interface or somewhere. If T is an interface

    public interface T {
        String getValue();
    }
    

    then you have something like this

    public static void print(List<? extends Object> list) {
        for (Object t : list) {
            if(t instanceof T) System.out.println(((T)t).getValue());
            else if (t instanceof List) print((List) t);
            else continue; // I don't know this type.
        }
    }
    

    But it is then attached to your Interface T. The Problem is really that you can't access methods, which are not defined, at least not without Reflections. How does C++ do this anyway?

    EDIT: This will of course not work, if you have an implementation of T which also implements the List interface.