【问题标题】:Generics wildcard isnt capable to cast to Generictype泛型通配符不能转换为 Generictype
【发布时间】:2024-01-19 14:00:01
【问题描述】:

我遇到了一个我无法找到解决方案的问题。

我经常使用映射,其中键和值是具有匹配泛型类型的对象。对于每一对,泛型应该匹配。尽管条目之间的泛型类型可能会有所不同。 (为了清楚起见,我会举一个例子)。 这可以通过使用通配符轻松完成。虽然正因为如此,您不能将键或值组合使用。

考虑底部的示例。没有(简单的)方法可以修改映射以遇到 Cast 异常。虽然我仍然无法像在useEntries() 中尝试过的那样使用地图。所以我的问题是,是否有解决方法?提前致谢!

public class GenericWildcardTest
{   
    static Map<GenericObject<?>, Function<?, ?>> map = new HashMap<>();

    public static <S> void put(GenericObject<S> genericObject, Function<S, S> function)
    {
        map.put(genericObject, function);
    }

    public static void useEntries()
    {
        for(Entry<GenericObject<?>, Function<?, ?>> currentEntry : map.entrySet())
            //The #apply(); part simply wont compile because of cast errors.
            currentEntry.getKey().set(currentEntry.getValue().apply(currentEntry.getKey().get()));
    }



    // Simple Object with generic.
    static class GenericObject<T>
    {
        private T object;

        public GenericObject(T object)
        {
            this.object = object;
        }

        public void set(T object)
        {
            this.object = object;
        }

        public T get()
        {
            return this.object;
        }
    }
}

【问题讨论】:

  • 强制转换是一种明显的解决方法。
  • 我对您的帖子和您的要求感到困惑。您正在使用泛型和通配符。我建议完整阅读本教程,它涵盖了泛型和通配符,并将阐明您的问题。 The Java™ Tutorials: Generics (Updated)
  • @shmosel 你可能根本没有尝试投射这个?因为它不起作用。我什至尝试用自己的通用实现创建一个私有方法。但它相当简单。泛型类型可以转换为通配符,但不能转换为 vicaversa
  • 我想你的意思是apply(currentEntry.getKey().get())。否则它需要是Function&lt;GenericObject&lt;S&gt;, S&gt;
  • currentEntry.getKey() 返回一个GenericObject&lt;?&gt;,这意味着编译器完全不知道? 是什么。为了保证你没有做错,它只允许你设置它知道是合法的,这完全没有。或者你能在不知道? 是什么的情况下推断出? 的相等或子类型吗?为什么您的地图不是(非静态)HashMap&lt;GenericObject&lt;S&gt;, ...&gt; 在本身类型为 &lt;S&gt; 的类中?那会奏效的。

标签: java generics casting wildcard classcastexception


【解决方案1】:

以下是您可以通过投射实现的方法:

@SuppressWarnings("unchecked")
public static <S> void useEntries() {
    for(Entry<GenericObject<?>, Function<?, ?>> currentEntry : map.entrySet()) {
        GenericObject<S> key = (GenericObject<S>)currentEntry.getKey();
        Function<S, S> value = (Function<S, S>)currentEntry.getValue();
        key.set(value.apply(key.get()));
    }
}

此答案假定您的地图确实包含Function&lt;S, S&gt;,而不是Function&lt;GenericObject&lt;S&gt;, S&gt;

【讨论】:

  • 我更喜欢单独对每个演员表进行抑制(例如@SuppressWarnings(...) GenericObject&lt;T&gt; key = ...)而不是整个方法,这样你就不会在以后更改方法时意外添加更多未经检查的演员表.但总的来说,这是比原始类型更好的方法。
  • @AndyTurner,同意。我实际上会更进一步,创建一个私有方法来避免暴露T(因此它实际上代表一种类型),但我决定在这里采用简单的方法。
【解决方案2】:

你可以重写useEntries方法如下:

@SuppressWarnings("unchecked")
public static void useEntries() {
    for (Map.Entry<GenericObject<?>, Function<?, ?>> currentEntry : map.entrySet()) {
        GenericObject key = currentEntry.getKey();
        Function value = currentEntry.getValue();
        key.set(value.apply(key.get()));
    }
}

从方法中的GenericObjectFunction 中删除泛型将允许您对纯Object 实例进行调用。那么您有责任确保正确输入。注释 SuppressWarning 将删除编译警告,否则会打印出来。

【讨论】:

  • 不要使用原始类型:使用强制类型转换。
  • 我建议在原始类型之前进行转换。由于原始类型,此代码中实际上存在一个错误。
  • @shmosel 一个bug,是什么?
  • @n247s 看到我的comment 来回答你的问题。
  • 但是是的,在这里投射肯定是更好的选择。
最近更新 更多