【问题标题】:How type inference work for method calls?方法调用的类型推断如何工作?
【发布时间】:2018-04-25 02:01:51
【问题描述】:

考虑以下示例:

public class Learn {
    public static <T> T test (T a, T b) {
        System.out.println(a.getClass().getSimpleName());
        System.out.println(b.getClass().getSimpleName());
        b = a;
        return a;
    }

    public static void main (String[] args) {
        test("", new ArrayList<Integer>());
    }
}

main 方法中,我使用StringArrayList &lt;Integer&gt; 对象调用test。两者是不同的东西,将ArrayList 分配给String(通常)会产生编译错误。

String aString = new ArrayList <Integer> (); // won't compile

但我在test 的第 3 行正是这样做的,程序编译并运行良好。首先我认为类型参数T 被替换为与StringArrayList 兼容的类型(如Serializable)。但是test 中的两个println 语句将“String”和“ArrayList”分别打印为ab 的类型。我的问题是,如果 ais Stringb 在运行时是 ArrayList,我们如何将 a 分配给 b

【问题讨论】:

  • 提示:您将ab 的静态类型与它们的动态类型混淆了。

标签: java generics


【解决方案1】:

对于泛型方法,Java 编译器将infer the most specific common type 用于参数ab

推理算法确定参数的类型,以及分配或返回结果的类型(如果可用)。最后,推理算法尝试找到适用于所有参数的最特定类型。

您没有将调用 test 的结果分配给任何东西,因此没有目标可以影响推理。

在这种情况下,即使StringArrayList&lt;Integer&gt; 有一个共同的超类型Serializable,所以T 被推断为Serializable,并且您始终可以将一个相同类型的变量分配给另一个。对于其他示例,您甚至可能会发现 Object 是常见的超类型。

但是仅仅因为你有 T 类型的变量被推断为 Serializable,对象本身仍然是 StringArrayList,所以获取它们的类并打印它们的名称仍然打印 StringArrayList。您没有打印变量的类型;您正在打印对象的类型。

【讨论】:

  • 嘿,谢谢。但是你能解释一下为什么程序打印的是“String”和“ArrayList”而不是“Object”和“Object”吗?
  • @DevashishJaiswal 推断的类型是 Serializable (或任何类型是 StringArrayList 的最具体的共同祖先),这就是它编译的原因,但在 runtime,类型还是StringArrayList
  • 感谢您的编辑。我这样做了:Object ob = new String (); System.out.println(ob.getClass().getSimpleName());。它打印“字符串”,现在我明白发生了什么。
  • @rgettman 我想我完全理解这一点,但为了更加确定......由于动态方法调度,代码会打印它打印的内容,对吧?
  • 这里没有动态调度效果。在运行时,动态分派用于尝试找到对对象的非静态方法调用的正确覆盖。但是在这里,没有类覆盖getClass,即Object 中的final。它已经定义为返回正确的Class 对象。对getName() 的调用在Class 对象上,Class 类本身就是final。因此,虽然从技术上讲,每个非静态 Java 对象方法调用都经过动态分派,但在这里实际上是无关紧要的。
【解决方案2】:
test("", new ArrayList<Integer>());

这等价于:

Learn.test("", new ArrayList<Integer>());

也等价于:

Learn.<Serializable>test("", new ArrayList<Integer>());

如果您显式指定Serializable(或Object)以外的泛型类型,则不会编译,例如String

Learn.<String>test("", new ArrayList<Integer>()); // DOES NOT COMPILE

所以在你的情况下,基本上这两个参数都被视为Serializable

【讨论】:

    猜你喜欢
    • 2013-08-13
    • 1970-01-01
    • 1970-01-01
    • 2011-06-20
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多