2

I want to get the real value of a generic class member (an array) in Java. I have googled it for several hours but have not found a clear answer. Honestly, I'm new to Java and this generic stuff.

public class Box<T> {
    private T t;

    public Box(T t) { this.t = t; }

    public void getTheT() {
        // get the value of t at index 0
        System.out.println(this.t[0]); // this doesn't work
    }
}

public class App {
    public static void main(String[] args) {
        Integer[] Arr = {2,3,4};
        Box<Integer[]> i = new Box<Integer[]>(Arr);
        i.getTheT();

    }
}

I've updated my question. Maybe this time it's clearer. How can I get the original value of t?

Boann
  • 48,794
  • 16
  • 117
  • 146
under5hell
  • 997
  • 3
  • 16
  • 40
  • 1
    it simply calls the `toString()` of your instance...which unfortunately looks like this for arrays – Manuel Jain Jun 08 '15 at 14:59
  • For generics, you have to always consider the most specific class it can take given your specification, which in this case is simply `Object`, not an array, so it can only call `Object.toString()`. – Dragondraikk Jun 08 '15 at 15:00
  • 1
    @Dragondraikk, that's not how inheritance works in Java, overriden `toString` will always be called. – user3707125 Jun 08 '15 at 15:01
  • @user3707125 Yes, I wasn't quite clear there. However, for an Array `toString()` will still simply be the object reference, which is not very helpful – Dragondraikk Jun 08 '15 at 15:02
  • 1
    @all: yes, i know that every object has toString method. But, what I'm really gonna do is to manipulate the value of t. Assuming I create the instance of box and pass Integer[] as parameter. Then i want to create a method that can change the t element at specific index. How can i do this if i cannot access the element of t? This case much simpler if i create normal Box class, but my requirement need it to be a generic class, which is confusing. – under5hell Jun 08 '15 at 15:07
  • `getTheT` is a getter it is supposed to return something ? where is `return` instruction ? –  Jun 08 '15 at 15:19
  • @OSryx: yes, it should return something. But for the sake of this question it's not important. That's why i put void there. All i want to know for now is how to access the value of t. – under5hell Jun 08 '15 at 15:22

4 Answers4

1

You should simply cast t to its original type Integer[], like this :

public class Box<T>{
    private T t;

    public Box(T arr){this.t = arr; } // you forget this semi-colon

    public void getTheT(){
        // get the value of t at index 0
        if(t.getClass() == Integer[].class){
            System.out.println("t[0]:"+((Integer[])t)[0]); // prints 2
        }
        else
            System.out.println(t);
    }

}


public class App{
    public static void main(String[] args){
        Integer[] Arr = {2,3,4};
        Integer i1 = 3;        
        Box<Integer> i = new Box<Integer>(i1);
        i.getTheT();
        Box<Integer[]> ia = new Box<Integer[]>(Arr);
        ia.getTheT();

    }
}
Boann
  • 48,794
  • 16
  • 117
  • 146
0

It clearly depends on the object type. The default implementation of toString which is called by System.out.println wil return this kind of information. It is the really value. If you want another formatting you'll have to do it by hand maybe by enclosing your array in an object.

class Custom {

    private Integer[] arr;
    //Constructor

    @Override
    public String toString(){
        String line = "";
        for (int i : arr) {
            line += i + ",";
        }
        //take off last ","
        return line;
    }
}
RPresle
  • 2,436
  • 3
  • 24
  • 28
0

"Value" here depends on how you define it. If you want to print the "value" of an Object in your case you may want to override the toString method. Since you can't override the implementation of an array you have to check for it. There are a lot of libraries around which can do this for you (in Apache Commons for example). In your case you can use the Arrays.deepToString() method.

Adam Arold
  • 29,285
  • 22
  • 112
  • 207
  • Yes, you right. but how to access the real value of t? In this case t is an Integer[] object. I cannot do this in standard way like this.i[0] since it's generic. it give me error "The type of expression must be array type but it resolve to T". – under5hell Jun 08 '15 at 15:12
  • What do you mean by "real value"? How do you define it? – Adam Arold Jun 08 '15 at 15:29
  • I've update my question Adam. Honestly, I'm new for this generic type in Java and maybe my question is not quiet clear. In my App Class I create new Box instance. Box is generic class which retrieve any type. In my App Class, I passed Integer[] Arr = {2,3,4} when I created the Box. It does make sense to me that the constructor copy the array into t (t is generic type). Now, for some reason I want to access that value (2,3,4). With non-generic type in non generic class case, I can simply do it by calling t[0] to access element at id 0. but it doesn't work with this generic type. – under5hell Jun 08 '15 at 15:40
0

Assuming your Box will always hold arrays then the simplest solution is to make your type parameter T be the element type instead of the array type (e.g., Integer instead of Integer[]) then make the private variable be of type T[] rather than T. Then Box will always have easy access to the array elements, and can also conveniently use any java.util.Arrays methods such as toString on it:

public class Box<T> {
    private T[] t;

    public Box(T[] t) { this.t = t; }

    public void getTheT() {
        System.out.println(t[0]); // access individual elements
        System.out.println(java.util.Arrays.toString(t)); // print the lot
    }
}

public class App {
    public static void main(String[] args) {
        Integer[] Arr = { 2, 3, 4 };
        Box<Integer> i = new Box<>(Arr); // not Box<Integer[]> any more
        i.getTheT();
    }
}

The catch is that since Java generics do not currently work with primitive types, you will have to put up with the overhead of object/boxed types (e.g., Integer instead of int).


If you want to keep T as the array type itself (perhaps because you want to allow Box<int[]>, since Box<int> is not possible) then it gets more tricky. In that case you can't simply use [...].

You can use the methods of java.util.reflect.Array to get access to arbitrary arrays' elements no matter what variable type they are stored in. This gives basic get and set access, but the methods are somewhat slow (fine for debugging but not for much else):

public class Box<T> {
    private T t;

    public Box(T t) { this.t = t; }

    public void getTheT() {
        // print all array elements
        for (int i = 0, len = java.lang.reflect.Array.getLength(t); i < len; i++) {
            System.out.println(java.lang.reflect.Array.get(t, i));
        }
    }
}

public class App {
    public static void main(String[] args) {
        int[] Arr = { 2, 3, 4 };
        Box<int[]> i = new Box<>(Arr); // primitive array element type!
        i.getTheT();
    }
}

Unfortunately there is currently no nice, performant way in Java to write code that can work with elements of all arrays, regardless of their types. Instead, such code has to be written nine times as you see the methods of java.util.Arrays are. That's once for element type Object (and everything that extends Object), and then once each for the primitive element types (boolean, byte, short, char, int, long, float, double).

Boann
  • 48,794
  • 16
  • 117
  • 146