在没有通用超级类型的情况下从不同对象获取值的通用方法

时间:2018-10-02 04:16:38

标签: java generics

我正在使用开放式API(因此无法更改其一侧的代码),该API提供了一系列枚举值类型。

User.IDType.values(); 

问题适用于上述数组中的每种类型。我必须调用value()函数来为提供的类型返回String值的数组。

public static String[] enumValues(IDType[] types) {
        String[] values = new String[types.length];
        for ( int i=0; i < types.length; i++ ) {
            values[i] = types[i].value();
        }

        return values;
    }

为了避免为每种类型编写此函数,我想编写如下的通用方法,但问题是:如何为通用类型调用函数值?

static <T> String[] enumValues(T[] types)
    {
        String[] values = new String[types.length];
        for (int i=0; i<types.length; i++) {
            values[i] = types[i].value;
        }

        return values;
    }
  

注意:任何类型都没有超类,例如:Type,我可以   使用。

3 个答案:

答案 0 :(得分:0)

通常,在声明类型变量时将其上限:

<T extends SomeType>

然后,您可以在方法主体中T类型的引用中从SomeType调用方法/访问字段。

但是,泛型对此方法一无所获。数组是协变的,因此您可以传递为什么元素类型为IDType的子类型的数组。

请勿混合使用泛型和数组。

答案 1 :(得分:0)

由于您有以下限制:

  

任何类型都没有超类,例如:我可以使用的类型。

剩下的解决方案是使用反射API。 但是,当更改value方法时,这种解决方案是不安全类型运行时错误,而不是编译时错误在枚举中。以下是使用反射API的示例。

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class GenericEnumTest {
    static <T> String[] enumValues(T[] types) {
        String[] values = new String[types.length];
        for (int i = 0; i < types.length; i++) {
            Method valueMethod;
            try {
                valueMethod = types[i].getClass().getMethod("value");
                values[i] = (String) valueMethod.invoke(types[i]);
            } catch (NoSuchMethodException | SecurityException | IllegalArgumentException | IllegalAccessException
                    | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
        return values;
    }

    public static void main(String[] args) {
        String[] result = enumValues(new IDType[] { IDType.TEST1, IDType.TEST2 });
        System.out.println(Arrays.toString(result));
    }

    public static enum IDType {
        TEST1("test1"), TEST2("test2");

        private IDType(String value) {
            this.value = value;
        }

        private String value;

        public String value() {
            return value;
        }
    }
}

参考:Java Doc Class.getMethod()
Java Doc Method class

答案 2 :(得分:0)

您可以使用Function<T, String>作为映射器从枚举值实例中提取String值。该映射器函数将如下传递给enumValues

   public static <T> String[] enumValues(T[] types, Function<T, String> mapper) {
        String[] values = new String[types.length];
        for (int i = 0; i < types.length; i++) {
            values[i] = mapper.apply(types[i]);
        }

        return values;
    }

用法很简单:

String[] ids = enumValues(User.IDType.values(), t -> t.value);

之所以可行,是因为从提供的枚举值数组的component type推断出映射器函数T的类型。因此t是一个枚举值实例,我们可以使用t的每个可见成员-在这种情况下为value

只要每个枚举值都有一个公共字段value,就可以使用t -> t.value。如果您使用方法value()代替字段value,则可以编写t -> t.value(),它等效于User.IDType::value。这表明显式的映射器函数取决于类型T。因此,如果枚举值类型不提供公共字段value,则映射t -> t.value将导致编译时错误。这意味着这种方法是类型安全的。

编辑
使用Stream-API,您可以按以下方式重写enumValues

public static <T> String[] enumValues(T[] types, Function<T, String> mapper) {
    return Arrays.stream(types).map(mapper).toArray(String[]::new);
}