Reusable method to transform Iterable to T[]?

后端 未结 3 554
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-20 11:17

I\'m trying to write a generic method to return the contents of an Iterable in array form.

Here is what I have:

public class IterableHelp
{
    publi         


        
相关标签:
3条回答
  • 2020-12-20 11:30

    You got a bigger problem than the unchecked warning. In fact, it will throw a ClassCastException at runtime if T is anything other than Object.

    Try String[] foo = IterableHelp.toArray(new ArrayList<String>());

    Simply put, since arrays contain the component type at runtime, to create a proper T[] you must pass in the component class as another argument (either as the class of T or T[] itself, or as an object of T or T[]), and use reflection to create the array. The form of Collection's toArray() method that takes an argument, takes in a T[] object for this reason.

    0 讨论(0)
  • 2020-12-20 11:43

    There's a method Iterables.toArray in Google Guava.

    Looking at the source, it's defined as:

      /**
       * Copies an iterable's elements into an array.
       *
       * @param iterable the iterable to copy
       * @param type the type of the elements
       * @return a newly-allocated array into which all the elements of the iterable
       *     have been copied
       */
      public static <T> T[] toArray(Iterable<? extends T> iterable, Class<T> type) {
        Collection<? extends T> collection = toCollection(iterable);
        T[] array = ObjectArrays.newArray(type, collection.size());
        return collection.toArray(array);
      }
    

    Where ObjectArrays.newArray eventually delegates to a method that looks like:

      /**
       * Returns a new array of the given length with the specified component type.
       *
       * @param type the component type
       * @param length the length of the new array
       */
      @SuppressWarnings("unchecked")
      static <T> T[] newArray(Class<T> type, int length) {
        return (T[]) Array.newInstance(type, length);
      }
    

    So it looks like there's no way to avoid the @SuppressWarnings entirely, but you can and should at least constrain it to the smallest possible scope.

    Or, better yet, just use somebody else's implementation!

    0 讨论(0)
  • 2020-12-20 11:43

    There isn't a way to get rid of the unchecked or unsafe operations warning, or creating a TypeSafe Array without editing your method signature.

    See this bug report for the gory details.

    One way is to pass in a pre-allocated Array of Type T:

    public class IterableHelp
    {
        public <T> T[] toArray(final T[] t, final Iterable<T> elements)
        {
            int i = 0;
            for (final T element : elements)
            {
                t[i] = element;
            }
            return t;
        }
    }
    

    This gets rid of the unchecked or unsafe warning, but it also puts the onus on the calling class to create the array with the correct bounds to begin with which kind of defeats the purpose of your convenience method.

    If you want to dynamically create a TypeSafe array, you really can't do it in a TypeSafe way in Java.

    This works and compiles, but it doesn't solve the unchecked or unsafe cast issue, it just moves it to a different place. I had to add the @SuppressWarnings annotation to get it to stop complaining about the cast.

    @SuppressWarnings({"unchecked"})
    public class IterableHelp
    {
        public <T> T[] toArray(Class<T> t, Iterable<T> elements)
        {
            final ArrayList<T> arrayElements = new ArrayList<T>();
            for (T element : elements)
            {
                arrayElements.add(element);
            }
            final T[] ta = (T[]) Array.newInstance(t, arrayElements.size());
            return arrayElements.toArray(ta);
        }
    }
    
    0 讨论(0)
提交回复
热议问题