【问题标题】:Java generics: put() on Map<String,capture#3-of ? extends AbstractClass> is not applicable for the arguments (String, AbstractClass)Java 泛型:在 Map<String,capture#3-of 上的 put() ? extends AbstractClass> 不适用于参数 (String, AbstractClass)
【发布时间】:2012-12-09 14:27:06
【问题描述】:

我正在尝试为从一个共同父类扩展的多个类实现一种实习生工厂。许多逻辑是相同的,但它不能真正被继承,因为查找需要是静态的。所需的语法类似于:

Car c = AbstractClass.valueOf(Car.class, "Ford");

Car 具有与汽车相关的特定方法,但实例存储在公共缓存中。这是我到目前为止所拥有的。我的编译错误在构造函数中:

“类型Map中的put(String, capture#3-of ? extends AbstractClass)方法不适用于参数(String, AbstractClass)”

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public abstract class AbstractClass {

    private static Map<Class<? extends AbstractClass>, LinkedHashMap<String, ? extends AbstractClass>> map = new HashMap<Class<? extends AbstractClass>, LinkedHashMap<String, ? extends AbstractClass>>();

    private static synchronized <T extends AbstractClass> Map<String, T> getNameMap(Class<T> clazz) {
        LinkedHashMap<String, T> nameToEnum = (LinkedHashMap<String, T>) map.get(clazz);
        if (nameToEnum == null) {
            nameToEnum = new LinkedHashMap<String, T>();
            map.put(clazz, nameToEnum);
        }

        return nameToEnum;
    }

    public static <T extends AbstractClass> T valueOf(Class<T> clazz, String name) {
        return getNameMap(clazz).get(name);
    }

    public static <T extends AbstractClass> Collection<T> VALUES(Class<T> clazz) {
        return getNameMap(clazz).values();
    }

    public static <T extends AbstractClass> Set<T> SORTED_VALUES(Class<T> clazz) {
        return new TreeSet<T>(getNameMap(clazz).values());
    }

    AbstractClass(String name) {
        AbstractClass.getNameMap(this.getClass()).put(name, this);
    }

}

【问题讨论】:

    标签: java generics capture


    【解决方案1】:

    根据Object.getClass() 的javadoc,返回的类型是表达式的基于通配符的编译时类型。由于编译器只知道this返回一个AbstractClass实例,所以this.getClass()返回Class&lt;? extends AbstractClass&gt;

    这意味着您在构造函数中对getNameMap 的调用将返回Map&lt;String, ? extends AbstractClass&gt;。这意味着,虽然返回的 Map 具有特定(非通配符)类型的值,但该确切类型在编译时是未知的;编译器只知道 Map 的值必须是 AbstractClass 或从 AbstractClass 继承的东西。所以编译器不能安全地将this 添加为一个值,因为在编译时不知道AbstractClass this 代表哪个子类型。

    举个更简单的例子:如果一个方法返回Map&lt;String, ? extends Number&gt;,那么编译器将不知道向映射添加一个整数是否安全,因为映射的实际非通配符类型可能是Map&lt;String, Double&gt;Map&lt;String, Short&gt;

    至于解决方案:我认为没有办法让 Map 使用泛型来匹配每个单独键的类型与其对应值的类型。我会忘记在内部 Maps 的值上使用有界类型,而是使用动态转换:

    private static Map<Class<? extends AbstractClass>, Map<String, AbstractClass>> map = new HashMap<>();
    
    private static synchronized Map<Class<? extends AbstractClass>, Map<String, AbstractClass>> getNameMap(Class<T> clazz) {
        // same as before
    }
    
    public static <T extends AbstractClass> T valueOf(Class<T> clazz, String name) {
        return clazz.cast(getNameMap(clazz).get(name));
    }
    

    【讨论】:

      【解决方案2】:

      如果您只想存储任何抽象类,只需将您的地图声明为

      private static Map<Class<? extends AbstractClass>, LinkedHashMap<String, AbstractClass>> map = 
          new HashMap<Class<? extends AbstractClass>, LinkedHashMap<String, AbstractClass>>();
      

      这将允许您在内部映射中存储 AbstractClass 或其子类的任何实例,以针对 AbstractClass 或其子类之一。

      【讨论】:

      • 如果我这样做,那么 getNameMap() 的返回类型不再是 和 valueOf() 方法不能返回实际类型。它只能返回必须转换为适当类型的 AbstractClass 类型。它应该能够工作,所以你可以这样做: Car car = AbstractClass.valueOf(Car.class, "Ford").
      【解决方案3】:

      你的问题基本上可以归结为:

      给定一个带有这个签名的方法:

      public static <T> void foo(T x, Class<T> y);
      

      以及任何引用类型的变量:

      <any reference type> bar;
      

      不可能将barbar.getClass()传递给这个方法:

      foo(bar, bar.getClass()); // error
      

      即使可以证明总是存在一些正确的T(即T = bar 的实际运行时类型)。

      这是由于 .getClass() 类型的语言中的特殊情况导致了此问题。

      我可以想到两种方法来解决这个问题:

      1) 将要参数化的类对象转换为与引用相同的类型(尽管这在技术上并不正确):

      AbstractClass(String name) {
          AbstractClass.getNameMap((Class<AbstractClass>)this.getClass()).put(name, this);
      }
      

      2) 将对象转换为与类方法的参数相同的类型。由于类类型中的通配符,这将需要一个捕获助手:

      private static <T> void helper(Class<T> clazz, String name, Object obj) {
          AbstractClass.getNameMap(clazz).put(name, (T)obj);
      }
      AbstractClass(String name) {
          helper(this.getClass(), name, this);
      }
      

      (如果你不想要那种未经检查的演员表,你可以这样做AbstractClass.getNameMap(clazz).put(name, clazz.cast(obj));

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-22
        • 2014-01-29
        • 2023-04-04
        • 2021-10-04
        • 1970-01-01
        • 2023-04-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多