Common Types & theArrayMethod

I have a class MyStack<T>

that defines the following

public T[] toArray(){
  int s=size();
  @SuppressWarnings("unchecked")
  T[] result=(T[])new Object[s];
  Node n=first;
  for (int i=0; i<s; i++){
     result[i]=n.data;
     n=n.next;
  }
  return result;
}

      

Since it returns an array of type T

, I would have thought that if I announced this instance: MyStack<String> s=new MyStack<>

then it would be quite rightly following: String[] test=s.toArray()

. I think this is because since it s

has a type String

, toArray

should return an array of type String

, since it has String

basically been replaced for everyone T

in that class (only for this specific one I know). The only way that this is done without mistakes - do the following: Object[] test=s.toArray()

.

Why is this?

+3


source to share


2 answers


Okay, hold on for a minute. Suppose your hypothesis is correct that you String

have substituted for each T

.

Will the next action take place?

String[] result = (String[])new Object[s];

      

No, it will not. We can be sure that a is new Object[]

not String[]

.

Now sometimes you will see something like (T[])new Object[n]

, but it only works because the actor is actually getting erased inside the generic class. (This is a deceiving idiom.)

When the class is compiled what actually happens is that the references to T

are replaced with an upper bound (perhaps Object

if you have something like <T extends ...>

):

public Object[] toArray(){
  int s=size();
  Object[] result=new Object[s];
  Node n=first;
  for (int i=0; i<s; i++){
     result[i]=n.data;
     n=n.next;
  }
  return result;
}

      



And the broadcast moves to the calling site:

MyStack stack = new MyStack();
String[] arr = (String[])stack.toArray();

      

So, in reality, when a cast is erased inside a class, a cast occurs as soon as the value is returned outside the class where it was ClassCastException

selected.

The inability to instantiate arrays (and objects in general) is generally the reason the Collections structure defines their method toArray

to return an array as an argument. A simple version of this would look like this for you:

public T[] toArray(T[] inArray){
    int s = size();

    Node n = first;
    for (int i = 0; i < s; i++){
        inArray[i] = n.data;
        n = n.next;
    }

    return inArray;
}

      

For some ideas on how to create a generic array , you can see "How do I create a generic array in Java?" ; however, you need the caller to pass some argument to the method.

+1


source


In short, erase the type. From the Java website:

Replace all type parameters in generic types with their bounds or an object if the type parameters are not constrained. Thus, the resulting bytecode contains only regular classes, interfaces, and methods.

This means that when the code is MyStack<String>

compiled, it will compile to MyStack<Object>

. This is to ensure that generics do not incur the overhead of requiring new classes to be created. How does this apply to you? Well..

MyStack<String> s = new MyStack<>();

      

converted to.



MyStack<Object> s = new MyStack<>();

      

This now means that when you call a method toArray

, the only type that can be guarenteed is the type Object

. The compiler cannot be sure that whatever it returns is of a type String

, so it won't let you treat it as String

because of strong typing in Java. So what is the only type of variable?

Object[] array = s.toArray();

      

Additional reading

+2


source







All Articles