Search code examples
javagenericsguavatoarray

Fluent Iterable to array from a list that contain generic classes


I have the following class structure:

abstract class Role;
class Employee extends Role;
class Student extends Role;
class Employer extends Role;

Also I have an enum in which I store the acceptable roles.

public enum RoleEnum
{
      EMPLOYEE ("Employee", Employee.class),
      STUDENT ("Student", Student.class), 
      EMPLOYER ("Employer", Employer.class);

      final private String name;

      final private Class<? extends Role> pRoleClass;

      private RoleEnum(final String name, final Class<? extends Role> pRoleClass)
      {
            this.name = name;
            this.pRoleClass = pRoleClass;
      }
}

What I want is to get an array of elements of type Class<? extends Role> from a list of RoleEnums. I am using FluentIterable from Guava libraries and what I am trying to do is something like FluentIterable.from(list).transform(function).toArray(Class<? extends PartyRole>). However what I managed to do is a workaround and kinda of a hack. It looks something like this:

(Class<R>[]) FluentIterable.from(roles)
                        .transform(new Function<RoleEnum, Class<R>>()
                        {
                            @Override public Class<R> apply(final RoleEnum role)
                            {
                                return (Class<R>) role.getRoleClass();
                            }
                        })
                        .toList().toArray(new Class[0]));

where

class RoleComboWidget<R extends Role>

is the class from where I call the method and

ImmutableList<RoleEnum> roles;

Solution

  • What you see is the effect of combining arrays with generics. Arrays in Java are fully typed at runtime: the component type is available at runtime and load/store operations are type checked and may throw exceptions.

    This requires the component type to be a reifiable type, but parameterized types like RoleComboWidget<W> are not. Generics are implemented in Java via invisible casts added at compile time, but once the program is running the JVM actually doesn't know if that is a List<String> or a List<Integer>.

    Back to your problem, if you are in control of the API, be careful when requiring arrays instead of Collection (or Stream): rarely the benefits (usually in performance) outperform the drawbacks (usually in source-code maintenance). Also, be warned (the compile already warns you) that the cast to SomeParameterizedType<T>[] is always unchecked: as per the things explained earlier, you could end up with objects of the wrong type that may screw up or even halt your program.