Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/iphone/36.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
具有泛型子类化的Java自动返回类型协方差_Java_Generics_Polymorphism_Subclass_Covariance - Fatal编程技术网

具有泛型子类化的Java自动返回类型协方差

具有泛型子类化的Java自动返回类型协方差,java,generics,polymorphism,subclass,covariance,Java,Generics,Polymorphism,Subclass,Covariance,我有两个界面如下所示: interface Parent<T extends Number> { T foo(); } interface Child<T extends Integer> extends Parent<T> { } 这是有道理的 如果我有一个原始的子对象对象,那么调用foo()将按照相同的逻辑返回一个整数。但是,编译器声明它返回一个数字 Child child = getRawChild(); Integer result = c

我有两个界面如下所示:

interface Parent<T extends Number> {
    T foo();
}

interface Child<T extends Integer> extends Parent<T> {
}
这是有道理的

如果我有一个原始的
子对象
对象,那么调用
foo()
将按照相同的逻辑返回一个
整数
。但是,编译器声明它返回一个
数字

Child child = getRawChild();
Integer result = child.foo(); // compiler error; foo() returns a Number, not an Integer
我可以覆盖
Child
中的
Parent.foo()

interface Child<T extends Integer> extends Parent<T> {
    @Override
    T foo(); // compiler would now default to returning an Integer
}
接口子级扩展父级{
@凌驾
T foo();//编译器现在默认返回整数
}
为什么会发生这种情况?有没有办法让
Child.foo()
默认返回一个
整数
,而不重写
Parent.foo()


编辑:假装
Integer
不是最终值。我刚刚选择了
Number
Integer
作为示例,但显然它们不是最佳选择:S

这两个
T
S并不完全相同。假设接口的定义是这样的:

interface Parent<T1 extends Number> {
    T1 foo();
}

interface Child<T2 extends Integer> extends Parent<T2> {
}
这是唯一允许的,因为Integer是Number的子类型。因此,父界面中的
foo()
签名(在子界面中扩展后)简化为:

interface Parent<T2 extends Number> {
    T2 foo();
}
接口父级{
t2foo();
}
换句话说,签名没有改变。在
Parent
接口中声明的方法
foo()
继续将
Number
作为原始类型返回

  • 这是基于@AdamGent的思想
  • 不幸的是,我对JLS不够流利,无法从规范中证明以下内容
  • 想象一下,
    公共接口父级
    是在一个不同的编译单元中定义的,位于一个单独的文件
    Parent.java

    然后,在编译
    Child
    main
    时,编译器将方法
    foo
    视为
    Number foo()
    。证明:

    import java.lang.reflect.Method;
    interface Parent<T extends Number> {
        T foo();
    }
    
    interface Child<R extends Integer> extends Parent<R> {
    }
    
    public class Test {
        public static void main(String[] args) throws Exception {
            System.out.println(Child.class.getMethod("foo").getReturnType());
        }
    }
    
    此输出是合理的,因为java不执行类型擦除,并且不能在结果
    中保留
    T extends
    。class
    文件plus,因为方法
    foo()
    仅在
    父级中定义。要更改子编译器中的结果类型,需要在
    child.class
    字节码中插入存根
    Integer foo()
    方法。这是因为编译后没有关于泛型类型的信息

    现在,如果您将您的孩子修改为:

    interface Child<R extends Integer> extends Parent<R> {
        @Override R foo();
    }
    
    这当然令人困惑,因为人们期望“词汇可见性”而不是“字节码可见性”


    另一种方法是编译器在两种情况下以不同的方式编译:在同一“词法范围”中的接口,编译器可以看到源代码;在另一个编译单元中的接口,编译器只能看到字节码。我不认为这是一个好的选择。

    我认为这是因为没有孩子的覆盖,孩子继承了父母的
    foo
    方法,根据父母的说法,
    t
    是一个
    数字。要使父对象中的
    foo
    返回一个
    Integer
    ,它需要知道有关其子类的信息,这类子类破坏了层次结构…请尝试
    接口子对象扩展父对象
    @fukanchik,它甚至不是有效的Java。@AdamGent谢谢您,它编译失败了。我浏览了JLS
    第4章。类型、值和变量
    ,找不到此处不允许使用上限的确切位置。@fukanchik它是一个。签名没有更改,但有一些语言可以进行泛型扩展(即复制/创建子类中的方法)。这与Java如何实现泛型有关。在Java语言中实现泛型类型的方式肯定有一些特殊性(Java在这方面不是唯一的),但通常可以预测并使用这些类型。我同意,但我可以理解OPs的困惑。我删除了我的答案,以支持你的答案。我不确定我的为什么会被否决(可能是因为我不明白OP想知道Java为什么选择这样实现泛型)。如果投票人只是加了一句话,那会有帮助的。但是你的答案和@Scottb更好。
    import java.lang.reflect.Method;
    interface Parent<T extends Number> {
        T foo();
    }
    
    interface Child<R extends Integer> extends Parent<R> {
    }
    
    public class Test {
        public static void main(String[] args) throws Exception {
            System.out.println(Child.class.getMethod("foo").getReturnType());
        }
    }
    
    class java.lang.Number
    
    interface Child<R extends Integer> extends Parent<R> {
        @Override R foo();
    }
    
    class java.lang.Integer