这是有效的Java吗? [英] Is this valid Java?

查看:22
本文介绍了这是有效的Java吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是有效的 Java 吗?

import java.util.Arrays;导入 java.util.List;类 TestWillThatCompile {public static String f(List list) {System.out.println("字符串");返回空;}公共静态整数 f(List list) {System.out.println("数字");返回空;}公共静态无效主(字符串 [] args){f(Arrays.asList("asdf"));f(Arrays.asList(123));}}

  • Eclipse 3.5 说
  • Eclipse 3.6 说
  • Intellij 9 说
  • Sun javac 1.6.0_20 表示
  • GCJ 4.4.3 说
  • GWT 编译器说
  • 我的之前的 Stackoverflow 问题

我的 Java 理论理解说

了解什么JLS 正在谈论它.

解决方案

这取决于您希望如何调用这些方法.如果您希望从其他 Java 源代码调用这些方法,那么由于 埃德温的回答.这是 Java 语言的限制.

然而,并非所有类都需要从 Java 源代码生成(考虑所有使用 JVM 作为其运行时的语言:JRuby、Jython 等...).在字节码级别,JVM 可以消除这两种方法的歧义,因为字节码指令指定了它们期望的返回类型.例如,这里有一个用 Jasmin 编写的类,它可以调用以下任一方法:

.class public CallAmbiguousMethod.super java/lang/Object.method public static main([Ljava/lang/String;)V.limit 堆栈 3.limit 本地人 1;调用返回String的方法aconst_nullinvokestatic TestWillThatCompile/f(Ljava/util/List;)Ljava/lang/String;;调用返回Integer的方法aconst_nullinvokestatic TestWillThatCompile/f(Ljava/util/List;)Ljava/lang/Integer;返回.end 方法

我使用以下命令将其编译为类文件:

<前>java -jar jasmin.jar CallAmbiguousMethod.j

并使用:

<前>java调用AmbiguousMethod

看,输出是:

<前>> java CallAmbiguousMethod字符串数字

更新

Simon 发布了一个调用这些方法的示例程序:

import java.util.Arrays;导入 java.util.List;类 RealyCompilesAndRunsFine {public static String f(List list) {返回列表.get(0);}公共静态整数 f(List list) {返回列表.get(0);}公共静态无效主(字符串 [] args){final String string = f(Arrays.asList("asdf"));最终整数整数 = f(Arrays.asList(123));System.out.println(string);System.out.println(整数);}}

这是生成的Java字节码:

<前>>javap -c RealyCompilesAndRunsFine从RealyCompilesAndRunsFine.java"编译class RealyCompilesAndRunsFine 扩展 java.lang.Object{RealyCompilesAndRunsFine();代码:0:aload_01:调用特殊#1;//方法java/lang/Object."":()V4:返回公共静态 java.lang.String f(java.util.List);代码:0:aload_01:iconst_02:调用接口#2、2;//InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;7:checkcast #3;//类java/lang/String10:返回public static java.lang.Integer f(java.util.List);代码:0:aload_01:iconst_02:调用接口#2、2;//InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;7:checkcast #4;//类 java/lang/Integer10:返回public static void main(java.lang.String[]);代码:0:iconst_11:新数组#3;//类java/lang/String4:重复5:iconst_06:ldc #5;//字符串asdf8:aastore9:调用静态#6;//方法java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;12:调用静态#7;//方法f:(Ljava/util/List;)Ljava/lang/String;15:astore_116:iconst_117:新数组#4;//类 java/lang/Integer20:重复21:iconst_022:双推12324:调用静态#8;//方法java/lang/Integer.valueOf:(I)Ljava/lang/Integer;27:aastore28:调用静态#6;//方法java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;31:调用静态#9;//方法f:(Ljava/util/List;)Ljava/lang/Integer;34:astore_235:获取静态 #10;//字段 java/lang/System.out:Ljava/io/PrintStream;38:aload_139:invokevirtual #11;//方法java/io/PrintStream.println:(Ljava/lang/String;)V42:获取静态#10;//字段 java/lang/System.out:Ljava/io/PrintStream;45:aload_246:invokevirtual #12;//方法java/io/PrintStream.println:(Ljava/lang/Object;)V49:返回

事实证明,Sun 编译器正在生成消除方法歧义所需的字节码(请参阅最后一个方法中的说明 12 和 31).

更新 #2

Java 语言规范表明这事实上,它可能是有效的 Java 源代码.在第 449 页(第 15.12 节方法调用表达式)上,我们看到:

<块引用>

可能没有最具体的方法,因为有两个或更多最具体的方法.在这种情况下:

  • 如果所有最大特定方法都具有覆盖等效(第 8.4.2 节)签名,然后:
    • 如果只有一个最大的特定方法没有被声明为抽象的,这是最具体的方法.
    • 否则,如果所有最大特定的方法都被声明为抽象的,并且所有最大特定方法的签名都相同擦除(第 4.6 节),然后在其中任意选择最具体的方法具有最特异性的最大特异性方法的子集返回类型.然而,最具体的方法被认为是抛出一个检查异常当且仅当该异常或其擦除在每个最大特定方法的 throws 子句.
  • 否则,我们说方法调用不明确,编译时发生错误.

除非我弄错了,这种行为应该只适用于声明为抽象的方法,不过......

更新 #3

感谢 ILMTitan 的评论:

<块引用>

@Adam Paynter:您的粗体文字确实如此没关系,因为这只是个案当两种方法是覆盖等效,丹展示了情况并非如此.就这样决定因素必须是 JLS考虑泛型类型时确定最具体的方法.——ILM泰坦

Is this valid Java?

import java.util.Arrays;
import java.util.List;

class TestWillThatCompile {

    public static String f(List<String> list) {
        System.out.println("strings");
        return null;
    }

    public static Integer f(List<Integer> list) {
        System.out.println("numbers");
        return null;
    }

    public static void main(String[] args) {
        f(Arrays.asList("asdf"));
        f(Arrays.asList(123));
    }

}

  • Eclipse 3.5 says yes
  • Eclipse 3.6 says no
  • Intellij 9 says yes
  • Sun javac 1.6.0_20 says yes
  • GCJ 4.4.3 says yes
  • GWT compiler says yes
  • Crowd at my previous Stackoverflow question says no

My java theory understanding says no!

It would be interesting to know what the JLS is saying about it.

解决方案

It depends upon how you wish to call these methods. If you wish to call these methods from other Java source code, then it is considered invalid for reasons illustrated in Edwin's answer. This is a limitation of the Java Language.

However, not all classes need to be generated from Java source code (consider all the languages that use the JVM as their runtime: JRuby, Jython, etc...). At the bytecode level, the JVM can disambiguate the two methods because the bytecode instructions specify the return type they are expecting. For example, here is a class written in Jasmin that can call either of these methods:

.class public CallAmbiguousMethod
.super java/lang/Object

.method public static main([Ljava/lang/String;)V
  .limit stack 3
  .limit locals 1

  ; Call the method that returns String
  aconst_null
  invokestatic   TestWillThatCompile/f(Ljava/util/List;)Ljava/lang/String;

  ; Call the method that returns Integer
  aconst_null
  invokestatic   TestWillThatCompile/f(Ljava/util/List;)Ljava/lang/Integer;

  return

.end method

I compile it to a class file using the following command:

java -jar jasmin.jar CallAmbiguousMethod.j

And call it using:

java CallAmbiguousMethod

Behold, the output is:

> java CallAmbiguousMethod
strings
numbers

Update

Simon posted an example program that calls these methods:

import java.util.Arrays;
import java.util.List;

class RealyCompilesAndRunsFine {

    public static String f(List<String> list) {
        return list.get(0);
    }

    public static Integer f(List<Integer> list) {
        return list.get(0);
    }

    public static void main(String[] args) {
        final String string = f(Arrays.asList("asdf"));
        final Integer integer = f(Arrays.asList(123));
        System.out.println(string);
        System.out.println(integer);
    }

}

Here is the Java bytecode generated:

>javap -c RealyCompilesAndRunsFine
Compiled from "RealyCompilesAndRunsFine.java"
class RealyCompilesAndRunsFine extends java.lang.Object{
RealyCompilesAndRunsFine();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return

public static java.lang.String f(java.util.List);
  Code:
   0:   aload_0
   1:   iconst_0
   2:   invokeinterface #2,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   7:   checkcast       #3; //class java/lang/String
   10:  areturn

public static java.lang.Integer f(java.util.List);
  Code:
   0:   aload_0
   1:   iconst_0
   2:   invokeinterface #2,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   7:   checkcast       #4; //class java/lang/Integer
   10:  areturn

public static void main(java.lang.String[]);
  Code:
   0:   iconst_1
   1:   anewarray       #3; //class java/lang/String
   4:   dup
   5:   iconst_0
   6:   ldc     #5; //String asdf
   8:   aastore
   9:   invokestatic    #6; //Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
   12:  invokestatic    #7; //Method f:(Ljava/util/List;)Ljava/lang/String;
   15:  astore_1
   16:  iconst_1
   17:  anewarray       #4; //class java/lang/Integer
   20:  dup
   21:  iconst_0
   22:  bipush  123
   24:  invokestatic    #8; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   27:  aastore
   28:  invokestatic    #6; //Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
   31:  invokestatic    #9; //Method f:(Ljava/util/List;)Ljava/lang/Integer;
   34:  astore_2
   35:  getstatic       #10; //Field java/lang/System.out:Ljava/io/PrintStream;
   38:  aload_1
   39:  invokevirtual   #11; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   42:  getstatic       #10; //Field java/lang/System.out:Ljava/io/PrintStream;
   45:  aload_2
   46:  invokevirtual   #12; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   49:  return

It turns out the Sun compiler is generating the bytecode necessary to disambiguate the methods (see instructions 12 and 31 in the last method).

Update #2

The Java Language Specification suggests that this may, in fact, be valid Java source code. On page 449 (§15.12 Method Invocation Expressions) we see this:

It is possible that no method is the most specific, because there are two or more methods that are maximally specific. In this case:

  • If all the maximally specific methods have override-equivalent (§8.4.2) signatures, then:
    • If exactly one of the maximally specific methods is not declared abstract, it is the most specific method.
    • Otherwise, if all the maximally specific methods are declared abstract, and the signatures of all of the maximally specific methods have the same erasure (§4.6), then the most specific method is chosen arbitrarily among the subset of the maximally specific methods that have the most specific return type. However, the most specific method is considered to throw a checked exception if and only if that exception or its erasure is declared in the throws clauses of each of the maximally specific methods.
  • Otherwise, we say that the method invocation is ambiguous, and a compiletime error occurs.

Unless I am mistaken, this behavior should only apply to methods declared as abstract, though...

Update #3

Thanks to ILMTitan's comment:

@Adam Paynter: Your bolded text does not matter, because it is only a case when two methods are override-equivalent, which Dan showed was not the case. Thus, the determining factor must be if the JLS takes generic types into account when determining most specific method. – ILMTitan

这篇关于这是有效的Java吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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