【问题标题】:generic types paired with method references与方法引用配对的泛型类型
【发布时间】:2018-12-20 21:48:22
【问题描述】:

我偶然发现了这个。这是短篇小说。在一些代码库中有一堆遵循相同模式的枚举,这里有一个大大简化的:

enum Letters {
    A("a"),
    Z("z");

    private String value;

    Letters(String value) {
        this.value = value;
    }

    private static final Map<String, Letters> MAP;

    public String getValue() {
        return value;
    }

    static {
        MAP = Arrays.stream(values())
                    .collect(Collectors.collectingAndThen(
                        Collectors.toMap(
                            Letters::getValue,
                            Function.identity()
                        ),
                        ImmutableMap::copyOf
                    ));
    }

    public static Optional<Letters> fromValue(String value) {
        return Optional.ofNullable(MAP.get(value));
    }
}

这是一个跨越数十个枚举的模式,从字面上看,这是无法更改的。我考虑过更改此代码重复,因为它很多

我的想法是从一个枚举接口开始,所有接口都会实现:

interface GetValueInterface {
    String getValue();
}

还有一些将处理此 MAP 创建的通用代码(fromValue 也将在那里处理,但让我们把它放在一边)。所以,是这样的:

 static class EnumsCommon {

    public static <T extends Enum<T> & GetValueInterface> Map<String, T> getAsMap(Class<T> clazz) {
        T[] values = clazz.getEnumConstants();

        return Arrays.stream(values)
                     .collect(Collectors.collectingAndThen(
                         Collectors.toMap(
                             T::getValue,
                             Function.identity()
                         ),
                         ImmutableMap::copyOf
                     ));
    }
}

所以代码现在看起来像这样:

enum Letters implements GetValueInterface {

    // everything else stays the same
    static {
        MAP = EnumsCommon.getAsMap(Letters.class);
    }
}

这编译得很好,但是当我在java-8下运行它时,抛出了一个异常:

原因:java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Enum;不是实现类型接口GetValueInterface的子类型

java-11 下运行相同的代码可以正常工作...

【问题讨论】:

  • 为什么是T::getValue 而不仅仅是GetValueInterface::getValue
  • @FedericoPeraltaSchaffner 我最初也尝试过,但结果相同
  • 好的,看来这是一个已修复的类型推断错误。解决方案(正如您在自己的回答中所展示的)是不使用方法引用并为 lambda 表达式提供具有显式类型的参数。
  • @FedericoPeraltaSchaffner 我不认为这是一个类型推断错误顺便说一句,但更多的内容被删除以及如何删除,它应该被删除到GetValueInterface,而不是枚举,如果会有两个接口,简单地交换它们也可以解决它,似乎

标签: generics lambda enums java-8 java-stream


【解决方案1】:

准备这个问题20分钟后,我google了一下,马上就找到了问题:| this one

修复很简单,只需使用 lambda 表达式:

(T t) -> t.getValue() // instead of T::getValue

【讨论】:

  • 如果你还想使用方法引用,你必须使用(Function&lt;GetValueInterface,String&gt;)GetValueInterface::getValue。由于 PECS 规则,它将被 toMap 收集器接受,但与 TEnum 不再有任何关系。这有点像this Q&A。顺便说一句,您可以考虑在接口中使用ClassValue 实例进行缓存,而不是在每个实现类中都有一个字段。
  • @Holger 很遗憾我没看懂ClassValue 的提议,其实我还是第一次听说
猜你喜欢
  • 1970-01-01
  • 2020-12-20
  • 2010-12-26
  • 2010-10-01
  • 1970-01-01
  • 2021-02-16
  • 1970-01-01
  • 2011-10-15
  • 1970-01-01
相关资源
最近更新 更多