【问题标题】:Is there a way to avoid the constructor passing the Class?有没有办法避免构造函数传递类?
【发布时间】:2011-07-28 11:50:27
【问题描述】:

考虑这个 HashMap 扩展(如果为 null,则在调用“get”时生成 V 类的实例)

public class HashMapSafe<K, V> extends HashMap<K, V> implements Map<K, V>{

    private Class<V> dataType;

    public HashMapSafe(Class<V> clazz){
        dataType = clazz;
    }
    @SuppressWarnings("unchecked")
    @Override
    public V get(Object key) {
        if(!containsKey(key)){
            try {
                put((K)key, dataType.newInstance());
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return super.get(key);
    }
}

它的用法是这样的

Map<String,Section> sections = new HashMapSafe<String,Section>(Section.class);
sections.get(sectionName); //always returns a Section instance, existing or new

在我看来,两次提供“Section”似乎有点多余,一次作为泛型类型,同时提供它的类。我认为这是不可能的,但是有没有实现 HashMapSafe,(保持相同的功能)所以它可以像这样使用?

Map<String,Section> sections = new HashMapSafe<String,Section>();

还是这样?:

Map<String,Section> sections = new HashMapSafe<String>(Section.class);

【问题讨论】:

  • 因此,谷歌搜索“java newInstance generic class”发现了几篇涉及 this.getClass() 的旧帖子,随后能够访问对象的泛型,从而获得这些泛型的类,建议您的第一个选项是可能的。见:stackoverflow.com/questions/75175/…

标签: java generics


【解决方案1】:

正如其他人已经指出的那样,由于类型擦除,您无法改进构造函数的使用,但是您应该能够通过使用静态工厂方法而不是构造函数来提高详细程度...

我不在编译器前面,第一次尝试时我永远无法正确获取方法类型参数,但它会像这样......

public static <K,V> Map<K,V> create( Class<V> cl )
{
    return new HashMapSafe<K,V>(cl);
}

...

Map<String,Section> sections = HashMapSafe.create(Section.class);

【讨论】:

    【解决方案2】:

    不,没有。 Java 没有Reified generics

    GuavaMapMaker#makeComputingMap() 的风格中提供了一个不错的解决方案

    Map<String, Integer> numbers = new MapMaker().makeComputingMap(new Function<String, Integer>() {
        public Integer apply(String key) {
            return 0;
        }
    });
    

    当密钥不存在时设置并返回0 而不是null(它是线程安全的,与您的解决方案相反)。

    【讨论】:

    • 这是一个相当不错的集合类 :) 我可以想象它作为缓存非常有用,无需正常工作。
    【解决方案3】:

    两者都不可能。第一个需要能够执行new V() 之类的操作,而这是无法完成的。第二个需要能够在运行时设置 V 类型,因为它是在构造函数中传递的,这也是无法完成的。请记住,泛型仅在编译时使用,它们会在运行时被擦除。

    【讨论】:

      【解决方案4】:

      我觉得这篇文章很有趣:Reflecting generics

      简而言之:

        public abstract class AbstractUserType<T> implements UserType {
          ...
          public Class returnedClass {
            ParameterizedType parameterizedType =
              (ParameterizedType) getClass().getGenericSuperClass();
           return (Class) parameterizedtype.getActualTypeArguments()[0];
          }
          ...
        } 
      

      【讨论】:

      • 请注意,这仅在您直接在类中声明固定泛型类型(如class MyMap extends MyAbstractMap&lt;String, Integer&gt;)时才有效。我想 OP 想保持他的类通用。
      • 您可以保持类的通用性,您只需像这样声明您的类new HashMapSafe&lt;String,Section&gt;() {} 注意额外的{} 您可以将类抽象化以避免忘记这样做。
      【解决方案5】:

      有没有办法避免构造函数传递类?

      简而言之,没有。

      根本原因是“类型擦除”。在运行时,HashMapSafe 的实现没有隐式知道与类型参数对应的类型。该信息被删除。如果您需要实现知道类型是什么,则需要将其作为与Class&lt;V&gt; 兼容的Class 对象传递。

      Map<String,Section> sections = new HashMapSafe<String,Section>();
      

      由于类型擦除,这不起作用;见上文。

      Map<String,Section> sections = new HashMapSafe<String>(Section.class);
      

      这不起作用,因为 Java 语言要求您在使用该语法时为所有类型参数指定类型(或通配符)。

      【讨论】:

        猜你喜欢
        • 2020-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-28
        • 1970-01-01
        • 2019-09-07
        • 1970-01-01
        • 2021-12-25
        相关资源
        最近更新 更多