Java编译器如何为具有多个界限的参数化类型选择运行时类型? [英] How does the Java compiler choose the runtime type for a parameterized type with multiple bounds?

查看:94
本文介绍了Java编译器如何为具有多个界限的参数化类型选择运行时类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想更好地理解Java编译器遇到如下方法的调用会发生什么情况.

I would like to understand better what happens when the Java compiler encounters a call to a method like the one below.

<T extends AutoCloseable & Cloneable>
void printType(T... args) {
    System.out.println(args.getClass().getComponentType().getSimpleName());
}

// printType() prints "AutoCloseable"

对我来说很清楚,运行时没有类型<T extends AutoCloseable & Cloneable>,因此编译器会做最少的错误操作,并创建一个具有两个绑定接口之一的类型的数组,并丢弃另一个接口.

It is clear to me that there is no type <T extends AutoCloseable & Cloneable> at runtime, so the compiler makes the least wrong thing it can do and creates an array with the type of one of the two bounding interfaces, discarding the other one.

无论如何,如果切换接口顺序,结果仍然相同.

Anyway, if the order of the interfaces is switched, the result is still the same.

<T extends Cloneable & AutoCloseable>
void printType(T... args) {
    System.out.println(args.getClass().getComponentType().getSimpleName());
}

// printType() prints "AutoCloseable"

这使我进行了更多研究,并查看了接口更改时会发生什么. 在我看来,编译器使用某种严格的顺序规则来确定哪个接口是最重要的,并且接口在代码中出现的顺序不起作用.

This led me to do some more investigation and see what happens when the interfaces change. It seems to me that the compiler uses some kind of strict order rule to decide which interface is the most important, and the order the interfaces appear in code plays no role.

<T extends AutoCloseable & Runnable>                             // "AutoCloseable"

<T extends Runnable & AutoCloseable>                             // "AutoCloseable"

<T extends AutoCloseable & Serializable>                         // "Serializable"

<T extends Serializable & AutoCloseable>                         // "Serializable"

<T extends SafeVarargs & Serializable>                           // "SafeVarargs"

<T extends Serializable & SafeVarargs>                           // "SafeVarargs"

<T extends Channel & SafeVarargs>                                // "Channel"

<T extends SafeVarargs & Channel>                                // "Channel"

<T extends AutoCloseable & Channel & Cloneable & SafeVarargs>    // "Channel"

问题: 当存在多个边界时,Java编译器如何确定参数化类型的varargs数组的组件类型?

Question: How does the Java compiler determine the component type of a varargs array of a parameterized type when there are multiple bounds?

我什至不知道JLS是否对此有任何评论,而且通过谷歌搜索发现的任何信息都没有涉及这个特定主题.

I'm not even sure if the JLS says anything about this, and none of the information I found by googling covers this particular topic.

推荐答案

通常,当编译器遇到对参数化方法的调用时,它可以推断类型(

Typically, when the compiler encounters a call to a parameterised method, it can infers the type (JSL 18.5.2) and can create a correctly typed vararg array in the caller.

规则主要是技术性的说法,即查找所有可能的输入类型并检查它们"(诸如void,三元运算符或lambda之类的情况). 其余的是常识,例如使用最特定的通用基类(

The rules are mostly technical ways of saying "find all possible input types and check them" (cases like void, ternary operator, or lambda). The rest is common sense, such as using the most specific common base class (JSL 4.10.4). Example:

public class Test {
   private static class A implements AutoCloseable, Runnable {
         @Override public void close () throws Exception {}
         @Override public void run () {} }
   private static class B implements AutoCloseable, Runnable {
         @Override public void close () throws Exception {}
         @Override public void run () {} }
   private static class C extends B {}

   private static <T extends AutoCloseable & Runnable> void printType( T... args ) {
      System.out.println( args.getClass().getComponentType().getSimpleName() );
   }

   public static void main( String[] args ) {
      printType( new A() );          // A[] created here
      printType( new B(), new B() ); // B[] created here
      printType( new B(), new C() ); // B[] which is the common base class
      printType( new A(), new B() ); // AutoCloseable[] - well...
      printType();                   // AutoCloseable[] - same as above
   }
}

  • JSL 18.2 指示如何处理类型推断的约束,例如将AutoCloseable & Channel简化为Channel. 但是规则并不能帮助回答这个问题.
    • JSL 18.2 dictates how to process the constrains for type inference, such as AutoCloseable & Channel is reduced to just Channel. But the rules do not help answer this question.
    • 从调用中获取AutoCloseable[]看起来很奇怪,因为我们无法使用Java代码来做到这一点. 但实际上,实际类型并不重要. 在语言级别,argsT[],其中T是同时为A和B的虚拟类型"(

      Getting AutoCloseable[] from the call may look weird, of course, because we can't do that with Java code. But in reality the actual type doesn't matter. At the language level, args is T[], where T is a "virtual type" that is both A and B (JSL 4.9).

      编译器只需要确保其用法符合所有约束,然后知道 logic 是正确的,并且不会出现类型错误(这是Java泛型的设计方式). 当然,编译器仍然需要创建一个 real 数组,并为此目的创建一个通用数组". 因此,警告"unchecked generic array creation" ( JLS 15.12.4.2 ).

      The compiler just needs to make sure its usages meet all constrains, and then it knows the logic is sound and there will be no type error (this is how Java generic is designed). Of course the compiler still need to make a real array, and for the purpose it creates a "generic array". Thus the warning "unchecked generic array creation" (JLS 15.12.4.2).

      换句话说,只要您仅传入AutoCloseable & Runnable,并且仅调用printType中的ObjectAutoCloseableRunnable方法,则实际的数组类型并不重要. 实际上,无论传入哪种数组,printType的字节码都是相同的.

      In other words, as long as you pass in only AutoCloseable & Runnable, and calls only Object, AutoCloseable, and Runnable methods in printType, the actual array type does not matter. In fact, printType's bytecodes would be the same, regardless of what kind of array is passed in.

      由于printType不在乎vararg数组类型,因此getComponentType()无关紧要. 如果要获取接口,请尝试 getGenericInterfaces() 会返回一个数组.

      Since printType doesn't care the vararg array type, getComponentType() doesn't and shouldn't matter. If you want to get the interfaces, try getGenericInterfaces() which returns an array.

      • 由于类型擦除( JSL 4.6 ),T的接口顺序确实会影响(表示的一部分( JSL 4.4 ).这意味着顺序是无关紧要的.
      • 无论如何,此擦除规则确实会导致一些极端情况,例如添加printType(AutoCloseable[])会触发编译错误,而添加printType( Runnable[])则不会.我认为这是意外的副作用,并且真的超出了范围.
      • P.S.考虑到我认为我精神错乱./en.wikipedia.org/wiki/Ovis_aries"rel =" nofollow noreferrer>羊白羊,查看源代码插入程序集,并且难以用英语代替J̶́S͡L̴̀来回答.我的理智评分是b҉ȩyon̨d͝r̨̡͝e̛a̕l̵numb͟ers͡.回头. lib̭̳͠͡ͅẹ̡̬̦̙f͓͉̼̻o̼͕̱͎̬̟̪r҉͏̛̣̼͙͍͍̠̫͙ȩ̵̮̟̱̫͚ ̢͚̭̹̳̣̩̱͠..t̷҉̛̫͔͉̥͎̬ò̢̱̪͉̲͎͜o̭͈̩̖̭̬ .. ̮̘̯̗l̷̞͍͙̻̻͙̯̣͈̳͓͇a̸̢̢̰͓͓̪̳͉̯͉̼͝͝t̛̥̪̣̹̬͔̖͙̬̩̝̰͕̖̮̰̗͓̕͢ę̴̹̯̟͉̲͔͉̳̲̣͝͞.̬͖͖͇͈̤̼͖́͘͢.͏̪̱̝̠̯̬͍̘̣̩͉̯̹̼͟͟͠.̨͠҉̬̘̹ͅ
      • Because of type erasure (JSL 4.6), the order of interfaces of T does affect (JSL 13.1) compiled method signature and bytecode. The first interface AutoClosable will be used, e.g. no type check will be done when AutoClosable.close() is called in printType.
      • But this is unrelated with type interference of method calls of the question, i.e. why AutoClosable[] is created and passed. Many type safeties are checked before erasure, thus the order does not affect type safety. This I think is part of what JSL means by "The order of types... is only significant in that the erasure ... is determined by the first type" (JSL 4.4). It means the order is otherwise insignificant.
      • Regardless, this erasure rule does cause corner cases such as adding printType(AutoCloseable[]) triggers compile error, when adding printType( Runnable[]) does not. I believe this is an unexpected side effect and is really out of scope.
      • P.S. Digging too deep may cause insanity, considering that I think I am Ovis aries, view source into assembly, and struggles to answer in English instead of J̶́S͡L̴̀. My sanity score is b҉ȩyon̨d͝ r̨̡͝e̛a̕l̵ numb͟ers͡. T͉͎̫͠u͍r̟̦͝n̪͓͓̭̯̕ ̱̱̞̠̬ͅb̯̠̞̩͎a̘̜̯c̠̮k. ̠̝͕b̭̳͠͡ͅẹ̡̬̦̙f͓͉̼̻o̼͕̱͎̬̟̪r҉͏̛̣̼͙͍͍̠̫͙ȩ̵̮̟̱̫͚ ̢͚̭̹̳̣̩̱͠..t̷҉̛̫͔͉̥͎̬ò̢̱̪͉̲͎͜o̭͈̩̖̭̬.. ̮̘̯̗l̷̞͍͙̻̻͙̯̣͈̳͓͇a̸̢̢̰͓͓̪̳͉̯͉̼͝͝t̛̥̪̣̹̬͔̖͙̬̩̝̰͕̖̮̰̗͓̕͢ę̴̹̯̟͉̲͔͉̳̲̣͝͞.̬͖͖͇͈̤̼͖́͘͢.͏̪̱̝̠̯̬͍̘̣̩͉̯̹̼͟͟͠.̨͠҉̬̘̹ͅ

      这篇关于Java编译器如何为具有多个界限的参数化类型选择运行时类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆