【问题标题】:Java generic type resolutionJava 泛型类型解析
【发布时间】:2016-02-11 02:22:46
【问题描述】:

我最近开始使用 Java,但在处理泛型类型时仍然感到困惑。以下是我遇到一些问题的简化场景。

我有一个类,其中包含一个使用类类型作为键的 Map 和该类对象的集合:

public class GenericListInside {
    private Map<Class<?>, List<?>> mapping = new HashMap<>();

    public <T> void addListing(Class<T> clazz, List<T> object) {
        mapping.put(clazz, object);
    }
}

我可以毫无问题地调用 addListing:

GenericListInside gli = new GenericListInside();
List<Foo> list = new ArrayList<>(); 
//add something to list
gli.addListing(Foo.class, list); 

现在我决定创建一个类来提供流畅的界面。比如:

with(Foo.class).use(list);

然后我来了:

public class FluidInserter<T> {
    Class<T> clazz;
    GenericListInside gli = new GenericListInside();

    public FluidInserter with (Class<T> clazz) {
        this.clazz = clazz;
        return this;
    }

    public <T> void use(List<T> list) {
        gli.addListing(clazz, list);
    }
}

但是当我尝试编译时,我得到:

错误:(18, 12) java: 类 util.GenericListInside 中的方法 addListing 不能应用于给定类型;
必需:java.lang.Class,java.util.List
找到:java.lang.Class,java.util.List
原因:推断类型不符合等式约束
推断:T
等式约束:T,T

这条消息有点令人困惑......谁能弄清楚我做错了什么?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    流体生成器的泛型方法采用泛型方法参数,但该参数与 clazz 字段的类型不同,尽管名称重叠。

    只需从您的方法声明中删除&lt;T&gt;,同时将List&lt;T&gt; 作为参数:

    public void use(List<T> list) {
        gli.addListing(clazz, list);
    }
    

    题外话:您不想在 with 方法中返回原始类型的 FluidInserter。将返回类型更改为:

    public FluidInserter<T> with (Class<T> clazz)
    

    【讨论】:

      【解决方案2】:

      问题在于您的 use(List&lt;T&gt; list) 方法的定义:

      public <T> void use(List<T> list) {
          gli.addListing(clazz, list);
      }
      

      在这里,您通过引入具有相同名称的方法范围类型参数隐藏类范围类型参数T

      应该是:

      public void use(List<T> list) {
          gli.addListing(clazz, list);
      }
      

      它应该编译得很好,因为T 已经在类级别上定义了。

      【讨论】:

        【解决方案3】:

        你的问题是以下声明:

        public <T> void use(List<T> list) {
            gli.addListing(clazz, list);
        }
        

        由于您已经在类级别定义了泛型&lt;T&gt;,因此无需对方法进行泛型化。

        我建议你把签名改成如下:

        public void use(List<? extends T> list) {
            // same logic here
        }
        

        另外,作为旁注,我认为您还应该将GenericListInside 设为通用。现在mapping 字段的定义允许在键和值中混合类型,这是您不想要的。

        编辑

        您可以像这样向GenericListInside 添加泛型:

        public class GenericListInside<T> {
        
            private final Map<Class<T>, List<T> mapping = new HashMap<>();
        
            public void addListing(Class<T> clazz, List<T> list) {
                mapping.put(clazz, list);
            }
        }
        

        【讨论】:

        • 谢谢丹尼尔!关于您的上一条评论:我真的很关心映射字段内部的类型,并希望 addListing 方法能够保证内聚。我不知道如何解决这个问题,使类变得通用。请您详细说明一下吗?
        • @marcellorvalle 查看我对答案所做的更新。
        • 乍一看,这会将我的地图限制为单一类类型。当我测试它时,我发现它不是真的。我需要更深入地了解泛型...谢谢!
        猜你喜欢
        • 2014-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多