首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java自身类型递归类型参数和javac中的继承错误

Java自身类型递归类型参数和javac中的继承错误
EN

Stack Overflow用户
提问于 2012-08-21 23:34:43
回答 3查看 1.9K关注 0票数 8

为什么这段代码不能编译?

代码语言:javascript
复制
public class x
{
    private void test()
    {
        handle(new ThingA());
        handle(new ModifiedThingA());
    }

    private <T extends BaseThing<T>, X extends T> java.util.List<T> handle(X object)
    {
        return object.getList();
    }

    private static class BaseThing<T extends BaseThing<T>>
    {
        public java.util.List<T> getList()
        {
            return null;
        }
    }

    private static class ThingA
        extends BaseThing<ThingA>
    {
    }

    private static class ModifiedThingA
        extends ThingA
    {
    }
}

Java6在handle(new ModifiedThingA());中给出了这个错误

代码语言:javascript
复制
x.java:6: <T,X>handle(X) in x cannot be applied to (x.ModifiedThingA)
            handle(new ModifiedThingA());
            ^

Java7甚至不喜欢handle(new ThingA());,这是Java7的输出:

代码语言:javascript
复制
x.java:5: error: invalid inferred types for T; inferred type does not conform to declared bound(s)
            handle(new ThingA());
                  ^
    inferred: ThingA
    bound(s): CAP#1
  where T,X are type-variables:
    T extends BaseThing<T> declared in method <T,X>handle(X)
    X extends T declared in method <T,X>handle(X)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends BaseThing<CAP#1> from capture of ?
x.java:6: error: invalid inferred types for T; inferred type does not conform to declared bound(s)
            handle(new ModifiedThingA());
                  ^
    inferred: ModifiedThingA
    bound(s): CAP#1
  where T,X are type-variables:
    T extends BaseThing<T> declared in method <T,X>handle(X)
    X extends T declared in method <T,X>handle(X)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends BaseThing<CAP#1> from capture of ?
2 errors

在我看来,javacModifiedThingA误认为BaseThing<ModifiedThingA>,而它实际上是一个BaseThing<ThingA>。这是我的bug还是javac的?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-08-22 02:48:28

javac的行为似乎是正确的。理论上,一个类型变量T就足够了。但是,您引入了第二个类型变量X来帮助进行类型推断。首先推断出X的参数,然后根据调用上下文推断出T的参数:

代码语言:javascript
复制
List<ThingA> a = handle(new ThingA());
List<ThingA> b = handle(new ModifiedThingA());

但是,您的调用上下文不会对返回类型施加任何限制。因此,编译器被forced来引入一个类型变量(CAP#1),并将null类型作为下限。T的参数将被推断为glb(BaseThing<CAP#1>) = BaseThing<CAP#1>。考虑到它的下限,X不是T的子类型。

有两到三种方法可以解决这个问题。

  1. 手动推断(好的,如果极少使用返回类型necessary)
  2. provide (需要另一个名称)来推断“重载”)
  3. 如果返回的列表是不可变的或者是防御性的副本,您可以断开类型参数T与其边界参数

的连接

我更喜欢选项3:

代码语言:javascript
复制
private <T extends BaseThing<T>> List<T> handle(BaseThing<? extends T> object) {
    return new ArrayList<T>(object.getList());
    // or (using guava's ImmutableList)
    return ImmutableList.copyOf(object.getList());
}

快乐的泛型。

票数 1
EN

Stack Overflow用户

发布于 2015-11-04 20:44:03

您的代码在javac 1.8.0_45和Eclipse4.1.1中可以很好地编译。

可能为了更好地编译lambda表达式而对Java 8中的类型推断算法所做的更改也解决了您的问题。

票数 1
EN

Stack Overflow用户

发布于 2013-01-22 00:48:40

在javac 1.7.0_09和1.7.0_11中似乎有一个bug,导致了这个问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12058384

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档