Java方法调用中与类型参数相关的问题

Java方法调用中与类型参数相关的问题,java,java-8,Java,Java 8,Java 8的Java语言规范在“示例4.11-1.类型的使用”中提供了一个带有类型参数的方法调用示例: void循环{ this.loop(s);//是方法调用的类型参数。 } 在该示例中,提供的类型参数是有意义的,但是显然,方法调用的类型参数也可能是冗余的,完全没有意义,甚至不需要涉及泛型。例如: void m() { } void test() { m(); this.m(); this.<Integer>m(); // Compiles and

Java 8的Java语言规范在“示例4.11-1.类型的使用”中提供了一个带有类型参数的方法调用示例:

void循环{
this.loop(s);//是方法调用的类型参数。
}
在该示例中,提供的类型参数是有意义的,但是显然,方法调用的类型参数也可能是冗余的,完全没有意义,甚至不需要涉及泛型。例如:

void m() { }

void test() {
    m(); 
    this.m(); 
    this.<Integer>m(); // Compiles and runs OK!
    this.<String, Byte, StringBuilder, Thread[], Throwable>m(); // Compiles and runs OK!
    <Integer>m(); // Won't compile: "illegal start of expression"
}
void m(){}
无效测试(){
m();
这个.m();
this.m();//编译并运行正常!
this.m();//编译并运行正常!
m();//将不编译:“表达式的开头非法”
}
我有几个问题:

  • 有人能提出Java允许这些冗余类型参数的有效理由吗?承认它们没有害处,在我看来,它仍然是编译器能够而且应该捕捉到的东西

  • 仅当带有类型参数的方法调用的前缀为“this”时,该代码才会编译。否则会出现“表达式的非法开始”错误。那是虫子吗?任何使用“this”的明确方法调用不也应该使用“this”吗

  • (这些问题的催化剂是我为某人提出的一个有趣的Java问题创建的bug报告。)

    2015年9月18日更新

  • 我向Oracle提出了这个问题。以下是他们的回答:
  • 这不是问题;使用与普通方法相同的规则检查方法引用-请注意,对于普通方法,您始终可以提供冗余类型参数:

    void m() { }
    this.<String>m(); //legal
    
    void m(){}
    这个.m()//合法的
    
    方法(和构造函数)引用仅继承此行为,如15.13所示: “”如果方法引用表达式的形式为ReferenceType::[TypeArguments]标识符,则可能适用的方法是要搜索的类型的成员方法,其具有§15.12.2.1中规定的适当名称(由标识符给出)、可访问性、arity(n或n-1)和类型参数arity(源自[TypeArguments])。"

  • 由于该回复确认了任务已在下文提供的信息(包括引用JLS的相关章节),我接受了该回答
  • 以下是方法调用的方式:

    列出了调用方法的以下方法

    • MethodName([ArgumentList])
      (注意:这没有类型参数)
    • TypeName.[TypeArguments]标识符([ArgumentList])
    • ExpressionName.[TypeArguments]标识符([ArgumentList])
    • Primary.[TypeArguments]标识符([ArgumentList])
    • super.[TypeArguments]标识符([ArgumentList])
    • TypeName.super.[TypeArguments]标识符([ArgumentList])
    所以,在Java语言规范中,带有表达式或类型名的方法可以有类型参数,但请注意,在第一个方法调用中,不能指定类型参数,因为它是非法的

    请注意,只有
    this
    不允许这样做,但是
    static
    调用和
    super
    方法调用也可以有类型参数,这些参数是完全合法的

    static void m() { }
    
    void test() {
       ClassName.<Integer>m();//Also compiles
    }
    
    static void m(){}
    无效测试(){
    ClassName.m();//也编译
    }
    
    除此之外,您将收到以下警告:

    非泛型方法m()未使用的类型参数

    代表以下的声明:

    本条款意味着非泛型方法可能是潜在的 适用于提供显式类型参数的调用。 事实上,它可能被证明是适用的。在这种情况下,类型 参数将被忽略

    它确实说,它可能(在运行时)被证明是适用的

    而且

    这一规则源于兼容性问题和可替代性原则。由于接口或超类可以独立于其子类型进行泛型,我们可以用非泛型方法重写泛型方法。但是,重写(非泛型)方法必须适用于泛型方法的调用,包括显式传递类型参数的调用。否则,子类型将无法替换其泛型超类型


    我不知道为什么允许这样做,但在Eclipse中,您会收到一条警告“类型测试的非泛型方法m()未使用的类型参数;不应使用参数对其进行参数化”,这意味着不要这样做。--
    this.m()
    在语法上是有效的,如果键入了
    m
    则有意义,但
    m()
    是完全无效的语法。请参阅第二个问题,因为某些解析/语法困难。例如,
    x@bayou.io你的链接非常相关。Gábor Bakos在那里给出的关于为什么允许“无效”使用类型参数的答案有一定的说服力,尽管它解决了一个非常罕见的情况。@skomisa-我认为Bakos的答案是完全没有根据的。从未有过与非泛型的可比性目标。您在JLS15.12.2.1中的引用肯定明确了允许/忽略类型参数的原因,尽管在不使用泛型且调用的方法没有参数的情况下,这似乎有点牵强。Andreas在上面的评论中指出,Eclipse至少给出了一个编译器警告。我使用的是OracleJDK8U60,它根本不提供任何警告。
    
    static void m() { }
    
    void test() {
       ClassName.<Integer>m();//Also compiles
    }