java - 为什么 javac 无法对以有界类型参数作为返回类型的静态方法的调用站点进行类型检查?

标签 java generics return-type

<分区>

为什么 javac 在此代码示例中不会因类型错误而中止

import java.util.List;

public class StaticMethodWithBoundedReturnType {
    static class Foo {
    }

    static class Bar extends Foo {
    }

    static <F extends Foo> F getFoo(String string) {
        …
    }

    public static void main(String[] args) {
        // Compiles without error, even though List does not extend Foo.
        List<Integer> list = getFoo("baz");
    }
}

显然 List 永远不可能是 Foo 的子类型。即使存在 List 的子类型,它会以某种方式扩展 Foo,然后在 的调用站点分配 list getFoo() 应该无效。我知道类型删除的存在。但是 javac 不应该能够看到 list 的类型不满足有界类型约束 extends Foo 并因此无法编译类型错误?

为什么 javac 无法对以有界类型参数作为返回类型的静态方法的调用站点进行类型检查?

看起来我可以通过以下轻微修改获得类型安全:

import java.util.List;

public class StaticMethodWithBoundedReturnType {
    static class Foo {
    }

    static class Bar extends Foo {
    }

    static <F extends Foo> F getFoo(String string, Class<F> clazz) {
        …
    }

    public static void main(String[] args) {
        // Does not compile \o/
        List<Integer> list = getFoo("baz", List.class);
    }
}

但这需要将 Class 参数添加到 getFoo() 中,该参数根本不在方法主体中使用。有没有更好的方法来实现类型安全?

最佳答案

要理解这一点,我们需要理解以下内容的实际含义:

static <F extends Foo> F getFoo(String string) {
    return null;
}

也就是说 getFoo返回必须从进行调用的上下文中推断出的某种类型的值。此外,它还规定了推断类型必须是 Foo 的子类型的约束。 .

null可分配给所有可能的引用类型,适合作为返回值。事实上,这是唯一可能返回的可能。

为了说明,请尝试以下变体:

import java.util.List;

public class StaticMethodWithBoundedReturnType {
    static class Foo {
    }

    static class Bar extends Foo {
    }

    static <F extends Foo> F getFoo(String string) {
        return new Bar();
    }

    public static void main(String[] args) {
        // Compiles without error, even though List does not extend Foo.
        List<Integer> list = getFoo("baz");
    }
}

这会产生编译错误

StaticMethodWithBoundedReturnType.java:11: error: incompatible types:
Bar cannot be converted to F
        return new Bar();
               ^

您可能会问:为什么 Bar 与 F 不兼容?

答案:因为F代表( R & ? extends Foo )其中 RgetFoo 结果的类型被分配给。并且,在 getFoo 内重新不​​知道什么R将。 (事实上​​,它可以有很多不同的类型!)

简而言之,类型签名

    <F extends Foo> F getFoo(String string)

有问题。但是,考虑一下:

static <F extends Foo> F getFoo(Class<F> clazz) {
    return class.newInstance();
}

这是合法的,并且将返回一个满足运行时类型安全(本地)的值。但是如果您尝试将其分配给 List<Integer>,您将然后得到预期的编译错误。 :

StaticMethodWithBoundedReturnType.java:16: error: incompatible types:
inference variable F has incompatible bounds
        List<Integer> list = getFoo(Bar.class);
                                   ^
    equality constraints: Bar
    upper bounds: List<Integer>,Foo
  where F is a type-variable:
    F extends Foo declared in method <F>getFoo(Class<F>)
1 error

回到示例,考虑调用:

 List<Integer> list = getFoo("baz");

这是合法的,因为结果的推断类型是交集类型 (List<Integer> & ? extends Foo) .事实上,这种交集类型是可以实现的;例如作为

 class Baz extends Bar implements List<Integer> { /* list methods */ }

(我们程序中的这个 Baz 类中没有实现这一事实并不重要。可能有一个。)

所以,我们可以编译程序了。当我们执行它时,list将被分配null ,这不是运行时类型违规。

关于java - 为什么 javac 无法对以有界类型参数作为返回类型的静态方法的调用站点进行类型检查?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51462595/

相关文章:

java - classLoader.getResource(filename) 不返回存在的文件(返回空)

java - Openshift - javabean 每两秒创建一次

java - hibernate 4 泛型 : @Suppress Warnings vs Infer Generic Type Arguments?

rust - 为什么 impl trait 不能用于返回多个/条件类型?

powershell - 返回类型之谜

java - Ant 可以根据继承层次结构对 JUnit 测试进行分类吗?

java - 使用 JSP/Servlet 在网页上显示 Excel 工作表?

typescript - 创建多个通用类型的交集类型

java - Java 中数组与集合的运行时安全性

java - Scala:具有具有通用返回类型的函数