Java: 泛型方法是如何工作的?

Java: How do generic methods work?

我有几种类型的 Objects 正在计算。这些对象彼此非常不同,但共同的特征是它们每个都有一个 Enum 常量来描述它们。

例如,Day.getDayOfWeek() 成员可能 return DayOfWeek.THURSDAYCard.getSuit() 可能 return Suit.SPADEDog.getBreed() 可能 return 枚举常量 Breed.LABRADOR 等....

现在,假设我有 List<T> 这些对象(List<Dog>List<Card> 等),我想根据它们的枚举来计算这些对象状态。单独而言,我可以执行以下操作:

    public int[] getSuitCount(List<Card> cardList, int enumSize) {
        int[] array = new int[enumSize];
        for (Card card : cardList) {
            ++array[ card.getSuit().ordinal() ];
        }
        return array;
    }

    public int[] getDayCount(List<Day> dayList, int enumSize) {
        int[] array = new int[enumSize];
        for (Day day : dayList) {
            ++array[ day.getDayOfWeek().ordinal() ];
        }
        return array;
    }

    public int[] getBreedCount(List<Dog> dogList, int enumSize) {
        int[] array = new int[enumSize];
        for (Dog fido : dogList) {
            ++array[ fido.getBreed().ordinal() ];
        }
        return array;
    }

虽然这可行,但它非常重复。以下是首选:

    public int[] getEnumCount(List<T> itemList, int enumSize
                             SomeFunction GENERIC_FUNCTION()  ) {

        int[] array = new int[enumSize];
        for (Class<T> item : itemList) {
            ++array[ item.GENERIC_FUNCTION().ordinal() ];
        }
        return array;
    }

...唯一的问题是,GENERIC_FUNCTION() 在调用它的每个成员中都有不同的名称。有没有办法表示泛型函数?当我说泛型时,我的意思是,有没有一种方法 GENERIC_FUNCTION() 可以表示 getSuit() 或 getBreed(),因为它们都是 return Enum<?>?

我希望能够将 List<Card>Card.getSuit() 参数传递给此函数,并获得正确的计数。这种结构我有几百个函数,所以这在可读性和维护性上是一个非常不可忽视的优化。

注意:我根本无法编辑这些 类 中的任何一个。我唯一能做的就是使用通用函数,如果存在的话。

如果您有 Java 8 编译器,您可以使用 lambda 表达式方便地执行此操作。

public int[] getEnumCount(List<T> itemList, int enumSize
                         Function<T, Enum<?>> enumGetterFunction) {

    int[] array = new int[enumSize];
    for (Class<T> item : itemList) {
        ++array[ enumGetterFunction.apply(item).ordinal() ];
    }
    return array;
}

Function 是一个类似于 CallableRunnable 的接口,但它接受一个参数和 returns 个结果。您可以使用 lambda 语法 ((Dog d) -> d.getBreed()) 创建 Function,这意味着您可以像这样调用 getEnumCount

int[] counts = getEnumCount(listOfDogs, Breed.values().length, (Dog d) -> d.getBreed());

或更短,通过使用方法参考(等同于上述):

int[] counts = getEnumCount(listOfDogs, Breed.values().length, Dog::getBreed);

如果你使用Java8,你应该可以使用method references

没有 Java 8(比方说 Java 7),或者只有泛型,这是完全可能的。与 lambda 或委托函数等效的泛型是一个功能接口,具有您可以调用的单个已定义方法。

首先定义一个功能通用接口,它会给你一个通用的调用方法,你可以为每个具体类型覆盖它:

interface GetEnumFromType<S> {
  Enum getEnum(S item);
}

现在你在你的计数方法中调用这个:

public static <T> int[] getEnumCount(List<T> itemList, int enumSize,
    GetEnumFromType<T> converter) {

  int[] array = new int[enumSize];
  for (T item : itemList) {
    ++array[ converter.getEnum(item).ordinal() ];
  }
  return array;
}

最后,您可以通过为调用实际 "enum" 方法(或具体的 class 的每个类型创建一个匿名 class 来调用它,如果您在很多地点):

int[] suitCounts = getEnumCount(cards, Suits.values().length,
    new GetEnumFromType<Card>(){
      public Enum getEnum(Card card) { return card.getSuit(); }
  });
int[] dayCounts = getEnumCount(days, DayOfWeek.values().length,
    new GetEnumFromType<Day>() {
      public Enum getEnum(Day day) { return day.getDayOfWeek(); }
  });
int[] dogCounts = getEnumCount(dogs, Breeds.values().length,
    new GetEnumFromType<Dog>(){
      public Enum getEnum(Dog fido) { return fido.getBreed(); }
  });